*题解: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;
}
/*
*/

浙公网安备 33010602011771号