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;
    }
    
  • 相关阅读:
    高可用——Keepalived安装部署使用详解
    Java—File类详解及实践
    MySQL—设置数据库(库、表等)不区分大小写
    MySQL—Mysql与MariaDB启停命令的区别
    Linux—微服务启停shell脚本编写模板
    SpringBoot—集成AOP详解(面向切面编程Aspect)
    Java—Map集合详解
    手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊
    Java高效编程:总结分享
    Redis的几种应用实战
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14605631.html
Copyright © 2011-2022 走看看