• [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;
    }
    
    
  • 相关阅读:
    LootCode-链表排序-Java
    Vue创建项目及基本语法 一
    七大设计原则、单一职责原则
    SpringBoot 将自制的Starter 发布到远程公服
    SpringBoot 系列
    Mysql Sql 语句练习题 (50道)
    一、SpringBoot学习笔记_Eclipse 安装 SpringBoot、配置Gradle
    七、垃圾回收_判断对象是否存活算法_可达性分析法详解
    从零开始搭建Django+vue前后端分离项目
    Airtest一:批量运行脚本、汇总报告、导出报告
  • 原文地址:https://www.cnblogs.com/pmt2018/p/11588465.html
Copyright © 2020-2023  润新知