1. 程式人生 > >USACO 2017 Open Platinum

USACO 2017 Open Platinum

Description:

有一個nn個點,mm條邊的圖,每個點都有顏色,顏色種類的範圍為[1,K][1,K]。 現在有qq個操作,每個操作將點xx的顏色改為kk,在每個操作後求不同顏色的最小距離。 n2105,m4105,K106,q2105,wi106n\le2\cdot 10^5,m\le4\cdot 10^5,K\le10^6,q\le2\cdot 10^5,w_i\le10^6

Solution:

  • 對於這個nn21052\cdot10^5的圖,大概已經沒有什麼圖論演算法可以解決問題了
  • 而且我們發現答案一定是給定的某一邊的邊權
  • 對於暴力的做法(Θ(mllogmq)\Theta(ml\log m q)),我們是將邊權排序,找最小不同顏色的邊
  • 而將邊排序,我們很容易聯想到最小生成樹,那麼答案是不是一定在最小生成樹上呢?
  • 我們可以來反證一下
  • 若答案是一條非樹(最小生成樹)邊,那麼該邊的兩點顏色不同,且這兩點之間一定有樹邊,且這些樹邊的邊權一定比這條非樹邊小,且一定會有至少一條不同顏色的點的邊。
  • 這樣,我們可以用線段樹來維護樹上每個點與它顏色為cc的兒子的最小距離。
  • 但是操作還要修改,就需要用一個全域性的multisetmultiset來維護答案。
  • 這樣複雜度是Θ(nlogn)\Theta(n\log n)

Code

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline void Rd(T &x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=2e5+2,INF=0x3f3f3f3f;

int n,m,K,q;
int col[N];

struct node{
	int x,y,w,id;
	bool operator<(const node &_)const{
		return w<_.w;
	}
}em[N<<1];

struct p40{
	void solve(){
		while(q--){
			int x,k;
			Rd(x),Rd(k);
			col[x]=k;
			REP(i,1,m){
				if(col[em[i].x]==col[em[i].y])continue;
				printf("%d\n",em[i].w);
				break;
			}
		}
	}
}p1;

struct p100{
	
	int fa[N];
	int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
	
	int qwq,head[N];
	struct edge{
		int to,nxt,w;	
	}E[N<<1];
	void addedge(int x,int y,int z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
	
	int Fa[N],dis[N];
	
	int res[N];
	multiset<int>ans;
	
	int tim,rt[N];
	int Lson[N*40],Rson[N*40],Mn[N*40];
	int cnt,Id[N*40];//葉子
	multiset<int>Leaf[N<<1];
	
	void Update(int l,int r,int x,int w,int op,int &p){
		if(!p) p=++tim,Mn[p]=INF;
		if(l==r){
			if(!Id[p])Id[p]=++cnt,Leaf[Id[p]].insert(INF);
			if(op)Leaf[Id[p]].insert(w);
			else Leaf[Id[p]].erase(Leaf[Id[p]].find(w));
			Mn[p]=*Leaf[Id[p]].begin();
			return; 
		}
		int mid=(l+r)>>1;
		if(x<=mid)Update(l,mid,x,w,op,Lson[p]);
		else Update(mid+1,r,x,w,op,Rson[p]);
		chkmin(Mn[p]=Mn[Lson[p]],Mn[Rson[p]]);
	}
	
	int Query(int l,int r,int L,int R,int p){
		if(!p || L>R)return INF;
		if(l==L && r==R) return Mn[p];
		int mid=(l+r)>>1;
		if(R<=mid)return Query(l,mid,L,R,Lson[p]);
		else if(L>mid)return Query(mid+1,r,L,R,Rson[p]);
		else return min(Query(l,mid,L,mid,Lson[p]),Query(mid+1,r,mid+1,R,Rson[p]));
	}
	
	void dfs(int x){
		for(int i=head[x];~i;i=E[i].nxt){
			int y=E[i].to;
			if(y==Fa[x])continue;
			Fa[y]=x;
			Update(1,K,col[y],dis[y]=E[i].w,1,rt[x]);
			dfs(y);
		}
	}
	
	void solve(){

		REP(i,1,n)fa[i]=i;
		memset(head,-1,sizeof(head));
		int num=0;
		REP(i,1,m){
			int x=em[i].x,y=em[i].y;
			int fx=find(x),fy=find(y);
			if(fx!=fy){
				fa[fx]=fy;
				addedge(x,y,em[i].w);
				addedge(y,x,em[i].w);
				++num;
			}
			if(num==n-1)break;
		}

		Mn[0]=INF;
		dfs(1);
		REP(i,1,n) res[i]=min(Query(1,K,1,col[i]-1,rt[i]),Query(1,K,col[i]+1,K,rt[i])),ans.insert(res[i]);
		
		while(q--){
			int x,k;
			Rd(x),Rd(k);
			
			ans.erase(ans.find(res[x]));
			res[x]=min(Query(1,K,1,k-1,rt[x]),Query(1,K,k+1,K,rt[x]));
			ans.insert(res[x]);
			
			if(x!=1){
				int f=Fa[x];
				ans.erase(ans.find(res[f]));
				Update(1,K,col[x],dis[x],0,rt[f]),Update(1,K,k,dis[x],1,rt[f]);
				res[f]=min(Query(1,K,1,col[f]-1,rt[f]),Query(1,K,col[f]+1,K,rt[f]));
				ans.insert(res[f]);
			}
			
			col[x]=k;
			printf("%d\n",*ans.begin());
		}
	}
}p2;	

int main(){
//	freopen("war.in","r",stdin);
//	freopen("war.out","w",stdout);
	Rd(n),Rd(m),Rd(K),Rd(q);
	REP(i,1,m){
		int a,b,c;
		Rd(a),Rd(b),Rd(c);
		em[i]=(node){a,b,c,i};
	}
	REP(i,1,n) Rd(col[i]);
	sort(em+1,em+1+m);
		
	if(m<=10000 && q<=10000)p1.solve();//O(mlogm+mq)
	else p2.solve();//O(nlogn)
	return 0;
}