CF1361A Johnny and Contribution 题解

思路

根据题意,很显然要先所有把 \(t_i=1\) 的点 \(i\) 全部标上数,之后再标 \(t_i=2\) 的点。很显然,标点的顺序是根据 \(t_i\) 的大小,从小到大以此标点的。

根据每个操作的性质,我们可以用一个队列来维护进行标点的顺序,先将所有 \(t_i=1\) 的点 \(i\) 全部压入队列,然后再将所有和他连边且满足标点后的值等于 \(t_j\) 的点 \(j\) 压入队列。

现在考虑怎样才能判断一个点是否满足题目中的限制条件。我们可以考虑用一个数组记录每个点当前和他连边的点中点权从 \(1\) 开始的连续区间长度。对于当前枚举的点,在遍历每一条边的时候更新相连的点的连续区间的长度,并判断相连的点的最大值是否满足 \(t_i\) 的条件,若满足条件直接压入队列即可。

因为是根据 \(t_i\) 的大小从小到大枚举,所以每个点的连续区间不会出现一开始是中断的但某次操作后变成连续的情况,即不会出现相连的点的值域由 \([1,3]\cup[4,5]\) 在某次操作后变为 \([1,5]\) 的情况,只会出现相连的点的值域由 \([1,3]\cup[4,5]\) 在某次操作后变为 \([1,3]\cup[4,6]\) 的情况。

代码

#include <bits/stdc++.h>
#define N 500010
using namespace std;
int n,m,p[N],top,col[N],t[N];
bool v[N];
vector <int> G[N];
queue <int> q;
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&t[i]);
		if(t[i]==1){
			q.push(i);
			col[i]=1;
			v[i]=1;
		}
	}
	while(!q.empty()){
		int x=q.front();
		q.pop();
		p[++top]=x;
		col[x]=t[x];
		for(auto to:G[x]){
			if (t[to]==t[x]){
				puts("-1");
				exit(0);
			}
			if(col[x]==col[to]+1){
				col[to]++;
			}
			if(col[to]+1==t[to]&&!v[to]){
				q.push(to);
				v[to]=1;
			}
		}
	}
	if(top!=n){
		puts("-1");
		exit(0);
	}
	for(int i=1;i<=n;i++){
		printf("%d ",p[i]);
	}
	return 0;
}

posted @ 2022-09-17 11:50  Dregen_Yor  阅读(34)  评论(0)    收藏  举报