P4121 [WC2005] 双面棋盘
2023-07-18 20:48:35
简化题意:
给你一个n*n的只有黑白两种颜色的棋盘,每次修改把某格的黑白互换,求每次修改后黑白各自的连通块个数。
解法
将棋盘转化成n行,每行单独处理,处理出黑色和白色分别的连通块个数(可用并查集)。
开一个n的线段树,每个叶子节点表示一行。
合并过程中,先将黑白的连通块数量相加作为合并后的连通块数量,再用并查集将颜色相同但是非同一个指向的并查集合并并减去一个连通块个数。
每次修改都重新修改对应行的并查集数组。
修改行和合并行复杂度都是
单次修改复杂度
总复杂度 $ O(nm\log ^2 n)
遇到的问题1:
并查集得开二维,但是如果直接开二维组存很麻烦而且容易出错,所以用了题解的一个小技巧,把二维压成一维,这样和一般的并查集没有什么区别。
遇到的问题2:
修改值后只修改该行的值不能正解,原因大概是其他还有的地方指向之前未修改过的行,因此要在pushup里将有关的fa[]全修改掉(直接全局修改会超时)。
待解决的小问题:
思考为什么直接重开fa[]数组会WA,而新建ls[],rs[]数组记录上下两行的数据对应的并查集终点并在中途修改可以通过此题。
Code
int n,m; int fa[40004]; inline int gl(int x,int y){ return n*(x-1)+y; } inline int find(int x){ if(x==fa[x])return x; return fa[x]=find(fa[x]); } struct line{ int up[205],down[205],bl,wh,hup,hdown;//h 针对单行位置 int ls[205],rs[205]; void recreate(){ //单行暴力修改 bl=wh=0; for(int i=1;i<=n;i++){ ls[i]=rs[i]=fa[gl(hup,i)]=gl(hup,i); bl+=up[i]; wh+=up[i]^1; } for(int i=2;i<=n;i++){ if(up[i]==up[i-1]){ ls[i]=rs[i]=fa[find(gl(hup,i))]=find(gl(hup,i-1)); bl-=up[i]; wh-=up[i]^1; } } } }tr[806]; inline void pushup(int root){ tr[root].bl=tr[root<<1].bl+tr[root<<1|1].bl; tr[root].wh=tr[root<<1].wh+tr[root<<1|1].wh; for(int i=1;i<=n;i++){ tr[root].ls[i]=tr[root<<1].ls[i]; tr[root].rs[i]=tr[root<<1|1].rs[i]; fa[tr[root<<1].ls[i]]=tr[root<<1].ls[i]; fa[tr[root<<1].rs[i]]=tr[root<<1].rs[i]; fa[tr[root<<1|1].ls[i]]=tr[root<<1|1].ls[i]; fa[tr[root<<1|1].rs[i]]=tr[root<<1|1].rs[i]; tr[root].up[i]=tr[root<<1].up[i]; tr[root].down[i]=tr[root<<1|1].down[i]; } tr[root].hup=tr[root<<1].hup; tr[root].hdown=tr[root<<1|1].hdown; for(int i=1;i<=n;i++){ int upfa=find(tr[root<<1].rs[i]),downfa=find(tr[root<<1|1].ls[i]); if(tr[root<<1].down[i]==tr[root<<1|1].up[i]&&upfa!=downfa){ fa[upfa]=downfa; tr[root].bl-=tr[root<<1].down[i]; tr[root].wh-=tr[root<<1].down[i]^1; } } for(int i=1;i<=n;i++){ tr[root].ls[i]=find(tr[root].ls[i]),tr[root].rs[i]=find(tr[root].rs[i]); } } void build(int root,int l,int r){ if(l==r){ for(int i=1;i<=n;i++){ tr[root].up[i]=tr[root].down[i]=read(); } tr[root].hup=tr[root].hdown=l; tr[root].recreate(); //printf("line %d: bl:%d wh:%d\n",l,tr[root].bl,tr[root].wh); return; } build(root<<1,l,mid),build(root<<1|1,mid+1,r); pushup(root); //printf("line %d-%d: bl:%d wh:%d\n",l,r,tr[root].bl,tr[root].wh); } void update(int root,int l,int r,int x,int y){ if(l==r){ tr[root].up[y]^=1; tr[root].down[y]^=1; tr[root].recreate(); //printf("line %d: bl:%d wh:%d\n",l,tr[root].bl,tr[root].wh); return; } if(mid>=x)update(root<<1,l,mid,x,y); else update(root<<1|1,mid+1,r,x,y); pushup(root); //printf("line %d-%d: bl:%d wh:%d\n",l,r,tr[root].bl,tr[root].wh); } int main(){ n=read(); build(1,1,n); m=read(); while(m--){ int x=read(),y=read(); update(1,1,n,x,y); printf("%d %d\n",tr[1].bl,tr[1].wh); } }
本文作者:NBestの思潮
本文链接:https://www.cnblogs.com/NBest/p/17686855.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步