CF1218G Alpha planetary system 题解
Part 1
设 \(w_x\) 表示点 \(x\) 的权值 \(\bmod 3\),\(c_x\) 表示 \(x\) 的所属集合编号(\(c_x \in \{0,1,2\}\)),\(v_i\) 表示第 \(i\) 条边的权值。
一个直接的想法是使所有 \(w_x=c_x\),当然这不一定能做到,具体见下文。
首先,根据套路,随便建一颗生成树出来。设根为 \(rt\)。
先将所有非树边权值设为 \(3\)。对于点 \(u\),若其子树内所有树边权值都确定了,则 \(u\) 到父亲的树边的权值可以唯一确定。
然而,\(rt\) 没有到父亲的边,此时要做出调整:
- 如果此时 \(w_{rt}=c_{rt}\),则方案已经合法。
- 否则,我们需要找到包含 \(rt\) 的一个环进行调整。
Part 2
提取出包含 \(rt\) 的一个环,设环上边的编号依次为 \(e_1,e_2,\dots,e_k\),其中 \(e_1,e_2\) 两条边均有一个端点是 \(rt\)。
偶环是没用的,因为在偶环上对权值调整只能形如 \(v_{e_1}+1,v_{e_2}+2,v_{e_3}+1,v_{e_4}+2,\dots\),无法改变任何一点的权值。
奇环是有用的,考虑如下调整:
- 当 \(w_{rt}+1\equiv c_{rt} \pmod 3\),执行 \(v_{e_1}+2,v_{e_2}+2,v_{e_3}+1,v_{e_4}+2,v_{e_5}+1,\dots\)。显然环上除了 \(rt\) 其他点的权值均未改变。
- 当 \(w_{rt}+2\equiv c_{rt} \pmod 3\),执行 \(v_{e_1}+1,v_{e_2}+1,v_{e_3}+2,v_{e_4}+1,v_{e_5}+2,\dots\)。
因此,当原图中存在奇环,将 \(rt\) 取奇环上的任意一点,一定可以构造出合法方案。
Part 3
如果没有奇环呢?容易想到原图是二分图。
考虑重新将所有点划分为 \(2\) 个集合,这可以通过二分图染色实现。设 \(c_x\) 表示 \(x\) 新的所属集合编号(\(c_x \in \{1,2\}\))。
不失一般性地设 \(c_{rt}=1\),还是像 Part 1 那样将处理树边,最后考虑 \(rt\) 的情况:
- \(w_{rt} \ne 2\)。此时方案已经合法。因为对于与 \(rt\) 之间有边的点 \(u\) 必有 \(w_u=2 \ne w_{rt}\)。
- \(rt\) 的度数 \(=1\)。此时方案已经合法。设与 \(rt\) 之间有边的点为 \(u\),显然 \(u\) 的度数 \(>1\),则 \(u\) 的权值必然 \(>\) \(rt\) 的权值。
- \(w_{rt}=2\) 且 \(rt\) 的度数 \(>1\)。选出与 \(rt\) 之间有边的两个点 \(u,v\),将 \((rt,u),(rt,v)\) 两条边的权值 \(+1\) 即可。调整后 \(w_{rt}=1,w_u=w_v=0\),可以证明满足要求。
代码实现不难。
#include <bits/stdc++.h>
using namespace std;
using i64=long long;
using u64=unsigned long long;
using db=double;
using pii=pair<int,int>;
using vi=vector<int>;
template<typename T>
inline T read(){
T x=0,f=0;char ch=getchar();
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) x=x*10+(ch-'0'),ch=getchar();
return f?-x:x;
}
#define rdi read<int>
#define rdi64 read<i64>
#define fi first
#define se second
#define pb push_back
const int N=2e5+10;
int n,m,typ[N];
char s[N];
struct Edge{int to,nxt,fl,w;}e[N];
int head[N],tot=1,deg[N];
void add_e(int x,int y){
e[++tot]={y,head[x],0,0};
head[x]=tot,++deg[x];
}
bool odd_lp;
int col[N];
void dfs(int x,int c=1){
col[x]=c;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(!col[y]) e[i].fl=e[i^1].fl=1,dfs(y,3-c);
else if(col[y]==col[x]) odd_lp=true;
}
}
namespace S1{
int sum[N];
void dfs1(int x,int fa){
for(int i=head[x];i;i=e[i].nxt){
if(!e[i].fl) continue;
int y=e[i].to;
if(y!=fa) dfs1(y,x),sum[x]+=e[i].w;
}
for(int i=head[x];i;i=e[i].nxt){
if(e[i].fl&&e[i].to==fa){
e[i].w=e[i^1].w=(col[x]+3-sum[x]%3)%3;
sum[x]=col[x];break;
}
}
}
void solve(){
int rt=find(col+1,col+n+1,1)-col;
dfs1(rt,0);
if(deg[rt]>1&&sum[rt]%3==2){
int i1=head[rt],i2=e[i1].nxt;
e[i1].w++,e[i1^1].w++,e[i2].w++,e[i2^1].w++;
}
}
}
namespace S2{
int rt,u,sum[N],tofa[N];
void dfs1(int x,int fa){
for(int i=head[x];i;i=e[i].nxt){
if(!e[i].fl) continue;
int y=e[i].to;
if(y!=fa) dfs1(y,x),sum[x]+=e[i].w;
}
for(int i=head[x];i;i=e[i].nxt){
if(e[i].fl&&e[i].to==fa){
e[i].w=e[i^1].w=(typ[x]+3-sum[x]%3)%3;
sum[x]=typ[x],tofa[x]=i;
break;
}
}
}
void adjust(){
if(sum[rt]%3==typ[rt]%3) return;
vi cyc;int x=u;
while(x!=rt) cyc.pb(tofa[x]),x=e[tofa[x]].to;
for(int i=head[rt];i;i=e[i].nxt)
if(!e[i].fl&&e[i].to==u) {cyc.insert(cyc.begin(),i);break;}
int val=(sum[rt]+1)%3==typ[rt]%3?2:1;
for(auto id:cyc) e[id].w+=val,e[id^1].w+=val,val=3-val;
}
void solve(){
for(int x=1;x<=n;x++){
bool fl=0;
for(int i=head[x];i;i=e[i].nxt)
if(col[e[i].to]==col[x]){
fl=1,rt=x,u=e[i].to;
break;
}
if(fl) break;
}
dfs1(rt,0),adjust();
}
}
void output(){
for(int i=2;i<=tot;i+=2){
int x=e[i^1].to,y=e[i].to,w=(e[i].w+2)%3+1;
cout<<x-1<<' '<<y-1<<' '<<w<<'\n';
}
}
int main(){
n=rdi(),m=rdi();
scanf("%s",s+1);
for(int i=1;i<=n;i++) typ[i]=s[i]-'X'+1;
for(int i=1;i<=m;i++){
int x=rdi()+1,y=rdi()+1;
add_e(x,y),add_e(y,x);
}
dfs(1);
if(odd_lp) S2::solve();
else S1::solve();
output();
return 0;
}