BZOJ 4777: [Usaco2017 Open]Switch Grass
4777: [Usaco2017 Open]Switch Grass
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 46 Solved: 10
[Submit][Status][Discuss]
题目:给定一张带权无向图,每个点有一个颜色,每次改变一个点的颜色,要求你在操作后输出这个图中最近异色点对之间的距离最近异色点对定义为:一对点颜色不同,且距离最小。
数据范围:N个点,M条无向边,Q次修改,颜色范围[1,k],边权L。N,M,Q≤200000,K≤N,1≤L≤106 .
想法:
发现1:答案肯定是某一边权。因为边权大于0,答案路径上肯定是先经过若干个相同颜色的点,最后再碰到不相同。所以只要取这条路径的末端两个点就好了....
发现2:对于原图的答案等价于其最小生成树图的答案。因为在一个环上,最大边权只可能变劣(画图看看嘛),满足最小生成树环切性。
所以问题变成了:给你一棵最小生成树,询问该时刻相邻异色点距离最小是多少。
在线搞:既然是棵树,每个节点用堆/set存下每个颜色中其儿子节点的距离。剩下好像就很明了....
离线搞:考虑一条边什么时候会作为答案。按边权从小到大考虑每条边,用并查集跳过已经有边的时间。用双向链表维护一个点的颜色时间段。
复杂度:O(n+q+mlogm) 如果用基数排序也许是线性算法?
#include<cstdio> #include<vector> #include<algorithm> const int len(200000); struct Data{int last,col,pre,suc;}look_v,look_u,look; std::vector<Data>Seg[len+10]; struct ABC{int a,b,c;bool bf;}L[len+10]; struct Node{int nd,nx;}bot[len*2+10]; int tot,first[len+10],depth[len+10]; int f[len+10],ans[len+10]; int n,m,k,q,x,y,col[len+10],last[len+10]; template <class T>void read(T &x) { x=0;bool f=0;char c=getchar(); while((c<'0'||c>'9')&&c!='-')c=getchar(); if(c=='-')f=1,c=getchar(); while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} x=f?-x:x; } template <class T>T abs(T x){return x<0?-x:x;} template <class T>T min(T a,T b){return a>b?b:a;} void swap(int &x,int &y){x^=y,y^=x,x^=y;} bool cmp(ABC A,ABC B){return A.c<B.c;} void add(int a,int b){bot[++tot]=(Node){b,first[a]};first[a]=tot;} int gf(int x) { int v=x;while(f[v]!=v)v=f[v]; for(int o;x!=v;x=o)o=f[x],f[x]=v; return v; } void Kruskal() { for(int i=1,fa,fb;i<=m;i++) { fa=gf(L[i].a); fb=gf(L[i].b); if(fa!=fb) { f[fa]=fb; L[i].bf=true; } } } void union_Seg(int x,int v) { int pre=Seg[x][v].pre,suc=Seg[x][v].suc,t=gf(Seg[x][v].last); look=Seg[x][v]; if(Seg[x][suc].last<=t)//中间这块不会再被访问到 { Seg[x][pre].suc=suc; Seg[x][suc].pre=pre; if(Seg[x][pre].col==Seg[x][suc].col) { Seg[x][pre].suc=Seg[x][suc].suc;//合并颜色相同的 Seg[x][Seg[x][pre].suc].pre=pre; } } } int main() { freopen("C.in","r",stdin); freopen("C.out","w",stdout); read(n),read(m),read(k),read(q); for(int i=1;i<=m;i++) read(L[i].a),read(L[i].b),read(L[i].c); for(int i=1;i<=n;i++) read(col[i]),f[i]=i,last[i]=0;//从零开始 std::sort(L+1,L+1+m,cmp); Kruskal(); for(int i=1,sz;i<=q;i++) { read(x),read(y); sz=Seg[x].size(); Seg[x].push_back((Data){last[x],col[x],sz-1,sz+1}); col[x]=y; last[x]=i; f[i]=i; } for(int i=1,sz;i<=n;i++) { sz=Seg[i].size(); Seg[i].push_back((Data){last[i],col[i],sz-1,sz+1}); Seg[i].push_back((Data){q+1,0,0,0});//边界 } f[q+1]=q+1; for(int i=1;i<=m;i++) if(L[i].bf) { x=L[i].a,y=L[i].b; for(int now=0,v=0,u=0;now<=q;) { now=gf(now+1); if(now>q)break; look=Seg[x][v]; for(int suc=Seg[x][v].suc;Seg[x][suc].last<=now;) look=Seg[x][suc],v=suc,suc=Seg[x][suc].suc;//可以被卡到O(q^2) look=Seg[y][u]; for(int suc=Seg[y][u].suc;Seg[y][suc].last<=now;) look=Seg[y][suc],u=suc,suc=Seg[y][suc].suc;//可以被卡到O(q^2) look_v=Seg[x][v]; look_u=Seg[y][u]; if(Seg[x][v].col!=Seg[y][u].col)ans[now]=L[i].c,f[now]=now+1;//共O(n) else now=min(Seg[x][ Seg[x][v].suc ].last,Seg[y][ Seg[y][u].suc ].last)-1;//可以被卡到O(q^2) union_Seg(x,v); union_Seg(y,u);//合并 简化 以保证不被卡成O(q^2) //合并后 v,u不变没影响 } } for(int i=1;i<=q;i++)printf("%d %d\n",i,ans[i]); return 0; }