*题解:CF2245D2 Construct an Array (Hard Version)

题目链接

解析

考虑绝对值最大的那个数,如果其为正数,那么它加上所有数的和都大于等于 \(0\);如果其为负数,那么它加上所有数的和都小于等于 \(0\)

后者的等于 \(0\) 很烦人,因为我们 \(o = 2\) 的限制不能取等,考虑将 \(o = 1\) 的条件强化为 \(a_i + a_j > 0\),这样绝对值最大的数加上其它数不会等于 \(0\)。于是就有这样一种构造策略,每次取限制均为 \(o = 1\) 或限制均为 \(o = 2\) 的元素,按照正负给其赋上当前能取的最大 / 最小值,并将该限制移除,将能取的值的范围缩小。在未加强的约束下,没有这样的元素则说明无解,因为一定会有一个绝对值最大的元素,若该元素 \(x\) 为负数且存在 \(-x\),那么 \(-x\) 的限制应当均为 \(o = 1\),其余情况显然。

实现上,可以将元素看成点,限制看成边,建两个图分别代表 \(o=1\)\(o=2\) 的限制。若编号为 \(u\) 的点在其中一个图中为孤立点,则在两个图中移除它及与它相连的边,并对它进行赋值。

时间复杂度 \(O(n + m)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2e5 + 5,M = 300000 + 5,K = 10000 + 5;

vector<int> g[3][N];
int d[3][N],res[N],vis[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);	
//	freopen("in.txt","r",stdin);
//	freopen("out1.txt","w",stdout);
	int T;
	cin>>T;
	while(T--){
		int n,m;
		cin>>n>>m;
		for(int i=1;i<=m;i++){
			int o,u,v;
			cin>>o>>u>>v;
			g[o][u].push_back(v);
			d[o][u]++;
			g[o][v].push_back(u);
			d[o][v]++;
		}
		queue<pii> q;
		for(int i=1;i<=n;i++){
			if(!d[1][i]){
				vis[i] = true;
				q.push({i,2});
			}else if(!d[2][i]){
				vis[i] = true;
				q.push({i,1});
			}
		}
		int k = 0;
		int cnt = 0;
		while(!q.empty()){
			int u = q.front().first,x = q.front().second;
			q.pop();
			if(x == 1){
				res[u] = (int)1e9 - k;
			}else{
				res[u] = (int)-1e9 + k;
			}
			k++;
			cnt++;
			for(int v : g[x][u]){
				d[x][v]--;
				if(!d[x][v] && !vis[v]){
					vis[v] = true;
					q.push({v,x ^ 3});
				}
			}
		}
		if(cnt != n){
			cout<<"NO\n";
		}else{
			cout<<"YES\n";
			for(int i=1;i<=n;i++){
				cout<<res[i]<<" \n"[i==n];
			}
		}
		for(int i=1;i<=n;i++){
			g[1][i].clear();
			g[2][i].clear();
			vis[i] = false;
			d[1][i] = d[2][i] = 0;
		}
	}
	return 0;
}
/*
*/
posted @ 2026-08-22 11:48  yutar  阅读(10)  评论(0)    收藏  举报