题解:CF1270G Subset with Zero Sum
前言
感觉题目条件明示做法,并加深了我对 CF 的本质是 PH 的刻板印象。
思路分析
因为不小心看见了标签有 graph,所以断定这是一道图论建模题。
然后发现有限制 \(a_i\in[i-n,i-1]\),发现这个是一个长度为 \(n\) 的区间,我们希望把它映射到 \([1,n]\),发现 \(i-a_i\in[1,n]\)。
对着这个东西仔细研究,发现如果连边 \(i \to i-a_i\),那么和为 \(0\) 的集合,就是图上的一个环。
考虑证明。
对于 \(p_1\to p_2\to \cdots\to p_k\to p_1\) 的环,满足:
\[p_k=p_1-\sum_{i=1}^{k-1}a_i
\]
\[p_1=p_k-a_k
\]
解得:
\[\sum_{i=1}^{k} a_i=0
\]
真是一个绝妙的猜测构造!
代码实现
#include<bits/stdc++.h>
using namespace std;
int t,n,a[1000005],vis1[1000005],vis2[1000005],flag;
stack<int> s;
int head[1000005],nxt[1000005],target[1000005],tot;
void add(int x,int y){
tot++;
nxt[tot]=head[x];
head[x]=tot;
target[tot]=y;
}
void dfs(int x){
if(flag) return;
vis1[x]=vis2[x]=1;
s.push(x);
for(int i=head[x];i;i=nxt[i]){
int y=target[i];
if(vis2[y]){
vector<int> v;
while(s.top()!=y){
v.push_back(s.top());
s.pop();
}
v.push_back(y);
cout<<v.size()<<'\n';
for(int i=0;i<v.size();i++){
cout<<v[i]<<' ';
}
cout<<'\n';
flag=true;
break;
}
if(vis1[y]) continue;
dfs(y);
if(flag) break;
}
vis2[x]=0;
if(s.size()) s.pop();
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
add(i,i-a[i]);
}
dfs(1);
flag=false;
for(int i=1;i<=n;i++){
vis1[i]=vis2[i]=0;
}
while(s.size()) s.pop();
}
return 0;
}