zoukankan      html  css  js  c++  java
  • [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;
    }
    
    
  • 相关阅读:
    Building Java Projects with Gradle
    Vert.x简介
    Spring及Spring Boot 国内快速开发框架
    dip vs di vs ioc
    Tools (StExBar vs Cmder)which can switch to command line window on context menu in windows OS
    SSO的定义、原理、组件及应用
    ModSecurity is an open source, cross-platform web application firewall (WAF) module.
    TDD中测试替身学习总结
    Spring事务银行转账示例
    台式机(华硕主板)前面板音频接口(耳机和麦克风)均无声的解决办法
  • 原文地址:https://www.cnblogs.com/pmt2018/p/11588465.html
Copyright © 2011-2022 走看看