正题
题目链接:http://noi.ac/problem/2007
题目大意
nnn个格子排成一排,每个格子有一个0/10/10/1和一个颜色。开始每个格子都是000,qqq次操作取反一个颜色的所有格子的0/10/10/1,然后询问111的格子构成的连通块数量。
1≤n,q≤1051\leq n,q\leq 10^51≤n,q≤105
解题思路
可以理解为总共的111格子数减去相邻的111格子对数。
转换一下模型,每队相邻的颜色x,yx,yx,y之间连接一条边。
现在问题变为了每次删除或者加入一个点,求连通子图的边的数量。
那么每次加入一个点xxx的时间复杂度是O(degx)O(deg_x)O(degx),这其实是有大量重复的,因为有一些点没有被加入但是也需要判断。
考虑平衡一下复杂度,发现对于一条边连接x,yx,yx,y,我们可以选择一个点在这个点修改的时候进行处理,若两个点的度数都在m\sqrt mm以内那么随便那个点处理这条边的情况就好了。若其中有一个点的度数大于m\sqrt mm的话,那么度数小的那个点处理。
然后两个点的度数都大于m\sqrt mm的话怎么办?不难发现这些点的数量不会超过m\sqrt mm,我们将重边压缩然后随便那个点处理都可以。
这样均摊下来时间复杂度O(qn)O(q\sqrt n)O(qn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+10;
int l,m,n,Q,w[N],c[N],deg[N],ans;
bool v[N],z[N];vector<int>e[N];
int main()
{scanf("%d%d%d",&l,&n,&Q);for(int i=1;i<=l;i++){scanf("%d",&c[i]);w[c[i]]+=1-(c[i]==c[i-1]);if(c[i]!=c[i-1])deg[c[i]]++,deg[c[i-1]]++,m+=2;}int T=sqrt(m);for(int i=1;i<=n;i++)z[i]=(deg[i]>T);for(int i=2;i<=l;i++){if(c[i]!=c[i-1]){int x=c[i],y=c[i-1];if(!z[x])e[x].push_back(y);else e[y].push_back(x);}}while(Q--){int x;scanf("%d",&x);int f=v[x]?-1:1;v[x]^=1;ans+=w[x]*f;for(int i=0;i<e[x].size();i++){int y=e[x][i];w[y]-=f;if(v[y])ans-=f;}printf("%d\n",ans);}return 0;
}