zoukankan      html  css  js  c++  java
  • NOI.AC#2007light【根号分治】

    正题

    题目链接:http://noi.ac/problem/2007


    题目大意

    \(n\)个格子排成一排,每个格子有一个\(0/1\)和一个颜色。开始每个格子都是\(0\)\(q\)次操作取反一个颜色的所有格子的\(0/1\),然后询问\(1\)的格子构成的连通块数量。

    \(1\leq n,q\leq 10^5\)


    解题思路

    可以理解为总共的\(1\)格子数减去相邻的\(1\)格子对数。

    转换一下模型,每队相邻的颜色\(x,y\)之间连接一条边。

    现在问题变为了每次删除或者加入一个点,求连通子图的边的数量。

    那么每次加入一个点\(x\)的时间复杂度是\(O(deg_x)\),这其实是有大量重复的,因为有一些点没有被加入但是也需要判断。

    考虑平衡一下复杂度,发现对于一条边连接\(x,y\),我们可以选择一个点在这个点修改的时候进行处理,若两个点的度数都在\(\sqrt m\)以内那么随便那个点处理这条边的情况就好了。若其中有一个点的度数大于\(\sqrt m\)的话,那么度数小的那个点处理。

    然后两个点的度数都大于\(\sqrt m\)的话怎么办?不难发现这些点的数量不会超过\(\sqrt m\),我们将重边压缩然后随便那个点处理都可以。

    这样均摊下来时间复杂度\(O(q\sqrt n)\)


    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;
    }
    
  • 相关阅读:
    JavaScript字面量
    JavaScript一元运算符、二元运算符和三元运算符
    JavaScript变量声明与变量声明提前
    JavaScript作用域链
    JavaScript变量污染
    语法糖
    JavaScript包装对象
    JavaScript全局变量与局部变量
    JavaScript标识符
    JS函数命名规范
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14605631.html
Copyright © 2011-2022 走看看