[USACO17OPEN Pt T2]Switch Grass 切换牧草

题目简述

给定一张(n,m(n,mle200,000))带权(权为正)无向图,每个点有一个颜色(k(kle n)),每次改变一个点的颜色,要求你在操作后输出这个图中最近异色点对之间的距离。最近异色点对定义为:一对点颜色不同,且距离最小.

传送门

解题报告

一道非常清新脱俗的US open题。

Part1

首先因为边权为正,显然可以想到答案一定是某一条边的权值。

进一步可以猜想答案一定是在图的最小生成树上。

给出一个证明:

考虑Kruskal的过程,我们先把边按边权排序,我们不断把边加入最小生成树的集合。

若某条连接(u,v)的边不能被加入集合(称为我们放弃的边),则这条边一定和某一条连接(u,v)的路径形成环。且环上所有边权都比这一条边小。

(u,v)两端颜色相同,则这一条边一定没有贡献

否则(u,v)颜色不同,在原有的(u,v)路径中,至少有一条边连接的两端颜色也不同,且那条边边权比我们放弃的边边权更小,显然更优。

Part 2

考虑维护这颗最小生成树。

我们先把它变成一颗有根树,然后考虑每个点 它的所有儿子 与他的连边的贡献

USACO官方题解给出了一种非常优美的解法(虽然写起来又臭又长)。

我们对于每一个点((u​))的所有儿子的颜色都开一个multiset,

我们称其为(CLS_{u,c}(c为颜色)​),存储它和所有颜色为(c​)的儿子的边长

显然每一条边只有可能在一个multiset里,总的节点数只有(n-1​),不会炸空间。(下面有具体实现)

再为每一个节点开一个multiset,称其为(best_u​),储存所有的((min{CLS_{u,c} })_{C in all colors }​)

我们再建一颗线段树,称其为(SGT​),储存每个节点和他儿子的最近异色点对长度。

所以对于每一个节点(u​),他的答案不是*(best[u]).begin()​就是*((best_u).begin()++)​

因为(best_u)中每种颜色显然只有一个,若第一个是同色,那第二个一定是异色(或者没有第二个)。

那我们如何维护呢?

首先,当对于每一个点的颜色(从(c​)改为(c'​))进行修改时,我们要修改两个部分。

  1. 考虑它和它父亲之间的影响。

    我们先要把它从原来在的(CLS_{fa[u],c}​)取出来,放进(CLS_{fa[u],c'}​)中,这时(min{CLS_{fa[u],c}}​)

(min{CLS_{fa[u],c'}}​)都会改变,我们还要修改(best_u​)中的这两项。

  1. 考虑改过颜色后,它和它儿子的答案可能由*(best[u]).begin()变为*((best_u).begin()++)

*((best_u).begin()++)变为*(best[u]).begin(),我们对其进行修改。

代码如下

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define fi first
#define se second

using namespace std;

typedef map<int,multiset<int> >::iterator MSIT;
typedef set<pair<int,int > >::iterator SPIT;


const int INF=0x3f3f3f3f, maxn=200007;
int n,m,q,k;
struct edge{
	int to,nxt,val;
}e[maxn<<1];
int head[maxn];
int tot;
void addedge(int u,int v,int val){
	e[++tot].to=v;
	e[tot].nxt=head[u];
	e[tot].val=val;
	head[u]=tot;
}//树边

namespace MST{
	struct edge1{
		int x,y,val;	
		friend bool operator <(const edge1 &a,const edge1 &b){
			return a.val<b.val;	
		}
	}G[maxn];
	int fa[maxn];
	int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
	void init(){for(int i=1;i<=n;i++)fa[i]=i;}
	void GMST(){
		init();
		sort(G+1,G+m+1);
		int cnt=0;
		for(int i=1;i<=m;i++){
			int u=G[i].x,v=G[i].y;
			int fu=find(u),fv=find(v);
			if(fu==fv)continue;
			cnt++,fa[fu]=fv;
			addedge(u,v,G[i].val);
			addedge(v,u,G[i].val);
		}
	}
}//求最小生成树
using namespace MST;

struct SGT{
	int t[maxn<<2];

	void pushup(int x){
		t[x]=min(t[x<<1],t[x<<1|1]);
	}
	void build(int x,int l,int r){
		if(l==r){
			t[x]=INF;
			return ;
		}
		int mid=(l+r)>>1;
		build(x<<1,l,mid);
		build(x<<1|1,mid+1,r);
		pushup(x);
	}
	void update(int id,int l,int r,int pos,int val ){
		if(l==r){
			t[id]=val;
			return ;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)update(id<<1,l,mid,pos,val);
		else update(id<<1|1,mid+1,r,pos,val);
		pushup(id);
	}
}T;//线段树
multiset<pair<int,int > > best[maxn];
map<int,multiset<int > > cls[maxn];
int cl[maxn];
int f[maxn];
int w[maxn];

void dfs(int u,int ff){
	f[u]=ff;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==ff)continue;
		w[v]=e[i].val;
		cls[u][cl[v]].insert(w[v]);
		dfs(v,u);
	}
	for(MSIT it=cls[u].begin();it!=cls[u].end();it++){
		best[u].insert(mp(*(*it).se.begin(),(*it).fi));//初始化best数组
	}
	//初始化线段树
	if(!best[u].empty()){
		SPIT it=best[u].begin();
		if(cl[u]!=(*it).se)T.update(1,1,n,u,(*it).fi);
		else{
			it++;
			if(it==best[u].end())T.update(1,1,n,u,INF);
			else T.update(1,1,n,u,(*it).fi);
		}
	}
		
}

int main(){

	scanf("%d%d%d%d",&n,&m,&k,&q);
	for(int i=1;i<=m;i++){
		int u,v,val;
		scanf("%d%d%d",&u,&v,&val);
		G[i]=(edge1){u,v,val};
	}	
	GMST();
	for(int i=1;i<=n;i++)scanf("%d",&cl[i]);
	T.build(1,1,n);
	dfs(1,0);
	while(q--){
		int u,c2;
		scanf("%d%d",&u,&c2);//每一次更改
		if(f[u]){
			int c1=cl[u];
			best[f[u]].erase(best[f[u]].find(mp(*cls[f[u]][c1].begin(),c1)));
			cls[f[u]][c1].erase(cls[f[u]][c1].find(w[u]));
			if(cls[f[u]][c1].empty())cls[f[u]][c1].insert(INF);
			best[f[u]].insert(mp(*cls[f[u]][c1].begin(),c1));
			//我们先把u从cl[u]抹去
			if(!cls[f[u]][c2].empty())
				best[f[u]].erase(best[f[u]].find(mp(*cls[f[u]][c2].begin(),c2)));
			cls[f[u]][c2].insert(w[u]);
			best[f[u]].insert(mp(*cls[f[u]][c2].begin(),c2));
			//再加入新的集合中
			SPIT it=best[f[u]].begin();
			if(cl[f[u]]!=(*it).se)T.update(1,1,n,f[u],(*it).fi);
			else{
				it++;
				if(it==best[f[u]].end())T.update(1,1,n,f[u],INF);
				else T.update(1,1,n,f[u],(*it).fi);
			}
		}
		if(!best[u].empty()){
			SPIT it=best[u].begin();
			if(c2!=(*it).se)T.update(1,1,n,u,(*it).fi);
			else{
				it++;
				if(it==best[u].end())T.update(1,1,n,u,INF);
				else T.update(1,1,n,u,(*it).fi);
			}
		}
		cl[u]=c2;
		printf("%d
",T.t[1]);
	}
	return 0;
}

原文地址:https://www.cnblogs.com/pmt2018/p/11588465.html