【Luogu】 P3665 [USACO17OPEN] Switch Grass P

题目链接

点击打开链接

题目解法

首先给出 2 个结论:

  1. 最接近的不同颜色的点一定是相邻的点
    证明:假设最接近的不同颜色的点 ( u , v ) (u,v) (u,v) 不相邻,那么 u , v u,v u,v 之间的路径中必有相邻的不同颜色点 ( u ′ , v ′ ) (u',v') (u,v) d i s ( u ′ , v ′ ) < d i s ( u , v ) dis(u',v')dis(u,v)<dis(u,v),矛盾
  2. 最接近的不同颜色的点一定在任意一个最小生成树上
    证明:假设最接近的不同颜色的点 ( u , v ) (u,v) (u,v) 不在最小生成树上(可以是任意一棵)
    根据 MST 的性质, u , v u,v u,v 之间的路径上的每一条边的 L e n t h < = l e n t h ( u , v ) Lenth<=lenth(u,v) Lenth<=lenth(u,v)
    因为 u , v u,v u,v 颜色不同,那么 u , v u,v u,v 的路径上必有相邻的不同颜色点,长度一定 ≤ l e n t h ( u , v ) \le lenth(u,v) lenth(u,v)

考虑建出一棵 MST
对于每个结点维护一棵线段树,在儿子的颜色信息上维护最小距离
同时在叶节点处用 m u l t i s e t multiset multiset 维护当前颜色的所有距离即可

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include 
using namespace std;
const int N(200100),inf(0x3f3f3f3f);
struct LINES{
	int x,y,z;
}lines[N];
int n,m,k,q,fa[N],col[N],FA[N],W[N],mndis[N];
int e[N<<1],w[N<<1],ne[N<<1],h[N],idx;
int tot,root[N],seg[N*38],lc[N*38],rc[N*38];
int tot2,dy[N*38];
bool leaf[N];
multiset<int> se[N<<1],ans; 
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
bool cmp(const LINES &x,const LINES &y){ return x.z<y.z;}
int get_father(int x){ return x==fa[x]?x:fa[x]=get_father(fa[x]);}
int insert(int p,int l,int r,int pos,int c){
	if(!p) p=++idx;
	if(l==r){
		if(!dy[p]) dy[p]=++tot2;
		se[dy[p]].insert(c);seg[p]=min(seg[p],c);
		return p;
	}
	int mid=(l+r)>>1;
	if(mid>=pos) lc[p]=insert(lc[p],l,mid,pos,c);
	else rc[p]=insert(rc[p],mid+1,r,pos,c);
	seg[p]=min(seg[lc[p]],seg[rc[p]]);
	return p;
}
void dfs(int u,int fa){
	leaf[u]=1,FA[u]=fa;
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		if(v!=fa) leaf[u]=0,dfs(v,u),W[v]=w[i],root[u]=insert(root[u],1,n,col[v],w[i]); 
	}
}
int query(int p,int l,int r,int L,int R){
	if(L>R||!p) return inf;
	if(L<=l&&r<=R) return seg[p];
	int mid=(l+r)>>1;
	if(mid>=L&&mid<R) return min(query(lc[p],l,mid,L,R),query(rc[p],mid+1,r,L,R));
	if(mid>=L) return query(lc[p],l,mid,L,R);
	return query(rc[p],mid+1,r,L,R);
}
void cover(int p,int l,int r,int pos,int w){
	if(l==r){
		se[dy[p]].erase(se[dy[p]].find(w));
		if(se[dy[p]].empty()) seg[p]=inf;
		else seg[p]=*se[dy[p]].begin();
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=pos) cover(lc[p],l,mid,pos,w);
	else cover(rc[p],mid+1,r,pos,w);
	seg[p]=min(seg[lc[p]],seg[rc[p]]);
} 
void add(int a,int b,int c){ e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}
int main(){ 
	n=read(),m=read(),k=read(),q=read();
	for(int i=1,x,y,z;i<=m;i++) x=read(),y=read(),z=read(),lines[i]={x,y,z};
	sort(lines+1,lines+m+1,cmp);
	for(int i=1;i<=n;i++) fa[i]=i;
	memset(h,-1,sizeof(h));
	for(int i=1,j=0;i<=m;i++){
		if(j==n-1) break;
		int fa0=get_father(lines[i].x),fa1=get_father(lines[i].y);
		if(fa0!=fa1){
			fa[fa0]=fa1,j++;
			add(lines[i].x,lines[i].y,lines[i].z),add(lines[i].y,lines[i].x,lines[i].z);
		}
	}
	memset(seg,0x3f,sizeof(seg));
	for(int i=1;i<=n;i++) col[i]=read();
	dfs(1,-1);
	for(int i=1;i<=n;i++) if(!leaf[i]) mndis[i]=min(query(root[i],1,n,1,col[i]-1),query(root[i],1,n,col[i]+1,n)),ans.insert(mndis[i]);
	while(q--){
		int x=read(),val=read();
		if(!leaf[x]){
			ans.erase(ans.find(mndis[x]));
			mndis[x]=min(query(root[x],1,n,1,val-1),query(root[x],1,n,val+1,n));
			ans.insert(mndis[x]);
		}
		if(x!=1){
			ans.erase(ans.find(mndis[FA[x]]));
			cover(root[FA[x]],1,n,col[x],W[x]);
			insert(root[FA[x]],1,n,val,W[x]);
			mndis[FA[x]]=min(query(root[FA[x]],1,n,1,col[FA[x]]-1),query(root[FA[x]],1,n,col[FA[x]]+1,n));
			ans.insert(mndis[FA[x]]);
		}
		col[x]=val;
		printf("%d\n",*ans.begin());
	}
	return 0;
}

你可能感兴趣的:(Luogu,算法)