【校内模拟】送分题(构造)

简要题意:

你有一张无向图,每个节点有颜色,每一轮变换中你需要为每一个点决定它在下一轮的颜色,可选颜色集合为它的颜色和它的所有邻居在这一轮中的颜色。

你需要用不超过20000轮操作来将100个节点的图的颜色从初始状态变为目标状态。

题解:

发现变换操作实际上非常强大,猜想对于一个连通块,只要初始颜色集合把目标态颜色集合包含了就不会有问题。

然后先找路径暴力调整颜色数,然后找路径暴力调整颜色位置。

由于路径长度最多是 O ( n ) O(n) O(n)的,这个做法的调整次数最坏不超过 2 n 2 2n^2 2n2


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=1e2+7,M=2e4+7;
int n,m,k;
int st[N],col[N],ed[N];
std::vector<int> G[N];
struct node{int t,u,v;};
//t=0 swap(col[u],col[v]) else col[u]=col[v]
std::vector<node> vec;
std::vector<int> nd;

int c1[N],c2[N];
int vis[N],h[N],id[N];

void dfs1(int u){
	nd.push_back(u);vis[u]=true;++c1[st[u]],++c2[ed[u]];
	for(int re v:G[u])if(!vis[v])dfs1(v);
}

bool dfs2(int u,int aim,int fa){
	if(u==aim)return true;h[u]=true;
	for(int re v:G[u])if(!h[v]&&dfs2(v,aim,u)){
		if(fa==-1)vec.push_back({1,u,v}),col[u]=col[v];
		else vec.push_back({0,u,v}),std::swap(col[u],col[v]);
		return true;
	}return false;
}

bool dfs3(int u,int aim,int lim){
	if(u==aim)return true;h[u]=true;
	for(int v:G[u])if(!h[v]&&id[v]<=lim&&dfs3(v,aim,lim)){
		std::swap(col[u],col[v]),vec.push_back({0,u,v});
		return true;
	}return false;
}

void solve(){
	while(true){
		bool ok=false;
		for(int u:nd)
		if(c1[col[u]]>c2[col[u]]){
			ok=true;
			for(int v:nd)
			if(c1[col[v]]<c2[col[v]]){
				c1[col[u]]--,c1[col[v]]++;
				memset(h,0,n<<2);
				dfs2(u,v,-1);break;
			}
			break;
		}
		if(!ok)break;
	}
	for(int re i=0;i<nd.size();++i)id[nd[i]]=i;
	for(int re i=nd.size()-1;~i;--i)
	if(col[nd[i]]!=ed[nd[i]]){
		for(int re j=0;j<i;++j)
		if(col[nd[j]]==ed[nd[i]]){
			memset(h,0,n<<2);
			dfs3(nd[i],nd[j],i);
			break;
		}
	}
}

signed main(){
#ifdef zxyoi
	freopen("hard.in","r",stdin);
#endif
	scanf("%d%d%d",&n,&m,&k);
	for(int re i=0;i<n;++i)scanf("%d",st+i);
	for(int re i=0;i<n;++i)scanf("%d",ed+i);
	for(int re i=1;i<=m;++i){
		int u,v;scanf("%d%d",&u,&v);--u,--v;
		G[u].push_back(v);
		G[v].push_back(u);
	}memcpy(col,st,n<<2);
	for(int re i=0;i<n;++i)if(!vis[i]){
		memset(c1,0,k<<2);
		memset(c2,0,k<<2);
		nd.clear();dfs1(i);
		for(int re j=0;j<k;++j)if(c2[j]&&!c1[j])
		puts("Impossible"),exit(0);solve();
	}
	for(int re i=0;i<n;++i)cout<<st[i]<<" ";cout<<"\n";
	for(auto &t:vec){
		if(t.t)st[t.u]=st[t.v];
		else std::swap(st[t.u],st[t.v]);
		for(int re i=0;i<n;++i)cout<<st[i]<<" ";cout<<"\n";
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值