[USACO17OPEN]Switch Grass P

Switch Grass P

题解 

由于答案一定是某一条边的边权,而两点只有当颜色相同时才会被忽略掉,所以可以发现这条边一定是在该图的最小生成树上。

于是我们可以先将这棵树给建出来,考虑每个点与他儿子连边的贡献。

我们可以先给每个点所有儿子的颜色开一个multiset,记其为CLS_{u,c},存储它和所有颜色为c的儿子的边长,而每条边只会被装进去一次,总共n-1条边,空间不会炸,再用一个multiset存储每个点的\min\{CLS_{u,c}\}

我们可以再用一棵线段树维护每个节点与其儿子的最近异色点对的连接边长。

所以对于一个节点,它的答案只会是前两者的大小,修改查询等操作都很容易维护。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
typedef long long LL;
const int INF=0x3f3f3f3f;
typedef pair<int,int> pii;
typedef map<int,multiset<int> >::iterator MSIT;
typedef set<pii>::iterator SPIT;
#define gc() getchar()
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
int n,m,k,q,tot,head[MAXN];
struct edge{int to,nxt,paid;}e[MAXN<<1];
void addEdge(int u,int v,int w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
namespace MST{
	struct edge1{
		int u,v,w;
		bool friend operator < (const edge1 &a,const edge1 &b){
			return a.w<b.w;
		}
	}G[MAXN];
	int fa[MAXN];
	void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
	int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
	void GMST(){
		makeSet(n);sort(G+1,G+m+1);int cnt=0;
		for(int i=1;i<=m;i++){
			int u=G[i].u,v=G[i].v;
			int fu=findSet(u),fv=findSet(v);
			if(fu==fv)continue;cnt++;fa[fu]=fv;
			addEdge(u,v,G[i].w);addEdge(v,u,G[i].w);
		}
	}
}
using namespace MST;
struct Segment_Tree{
	int tr[MAXN<<2];
	void pushup(int rt){tr[rt]=min(tr[rt<<1],tr[rt<<1|1]);}
	void build(int rt,int l,int r){
		if(l==r)return (void)(tr[rt]=INF);
		int mid=l+r>>1;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		pushup(rt);
	}
	void update(int rt,int l,int r,int ai,int aw){
		if(l==r)return (void)(tr[rt]=aw);
		int mid=l+r>>1;
		if(ai<=mid)update(rt<<1,l,mid,ai,aw);
		else update(rt<<1|1,mid+1,r,ai,aw);
		pushup(rt);
	}
}Tree;
multiset<pii>bst[MAXN];
map<int,multiset<int> >cls[MAXN];
int cl[MAXN],f[MAXN],ww[MAXN];
void dfs(int u,int father){
	f[u]=father;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==father)continue;
		ww[v]=e[i].paid;cls[u][cl[v]].insert(ww[v]);
		dfs(v,u);
	}
	for(MSIT it=cls[u].begin();it!=cls[u].end();it++)
		bst[u].insert(make_pair(*(*it).second.begin(),(*it).first));
	if(!bst[u].empty()){
		SPIT it=bst[u].begin();
		if(cl[u]!=(*it).second)Tree.update(1,1,n,u,(*it).first);
		else{
			it++;
			if(it==bst[u].end())Tree.update(1,1,n,u,INF);
			else Tree.update(1,1,n,u,(*it).first);
		}
	}
}
signed main(){
	scanf("%d %d %d %d",&n,&m,&k,&q);
	for(int i=1;i<=m;i++){
		int u,v,w;read(u);read(v);read(w);
		G[i]=(edge1){u,v,w};
	}
	GMST();
	for(int i=1;i<=n;i++)read(cl[i]);
	Tree.build(1,1,n);dfs(1,0);
	while(q--){
		int u,c2;read(u);read(c2);
		if(f[u]){
			int c1=cl[u];
			bst[f[u]].erase(bst[f[u]].find(make_pair(*cls[f[u]][c1].begin(),c1)));
			cls[f[u]][c1].erase(cls[f[u]][c1].find(ww[u]));
			if(cls[f[u]][c1].empty())cls[f[u]][c1].insert(INF);
			bst[f[u]].insert(make_pair(*cls[f[u]][c1].begin(),c1));
			if(!cls[f[u]][c2].empty())
				bst[f[u]].erase(bst[f[u]].find(make_pair(*cls[f[u]][c2].begin(),c2)));
			cls[f[u]][c2].insert(ww[u]);
			bst[f[u]].insert(make_pair(*cls[f[u]][c2].begin(),c2));
			SPIT it=bst[f[u]].begin();
			if(cl[f[u]]!=(*it).second)Tree.update(1,1,n,f[u],(*it).first);
			else{
				it++;
				if(it==bst[f[u]].end())Tree.update(1,1,n,f[u],INF);
				else Tree.update(1,1,n,f[u],(*it).first);
			}
		}
		if(!bst[u].empty()){
			SPIT it=bst[u].begin();
			if(c2!=(*it).second)Tree.update(1,1,n,u,(*it).first);
			else{
				it++;
				if(it==bst[u].end())Tree.update(1,1,n,u,INF);
				else Tree.update(1,1,n,u,(*it).first);
			}
		}
		cl[u]=c2;printf("%d\n",Tree.tr[1]);
	}
    return 0;
}

谢谢!!!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值