G.
把灯看成节点,灯之间的关系看成有向边
得到基环树森林
若节点的入度为0,那么它一定要用一次开关,这是可以确定的
所以用拓扑,把这些确定贡献的节点删去
然后就剩下了若干个环
若环上有奇数个权值为1的节点,那么不可能全部关上
对于环上一个打开的灯,它要么直接用自己的开关关上,要么等其他灯对它的影响,这两种情况都跑一次,取最小值即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
void solve() {
int n;cin>>n;
vector<int>idg(n+1),tag(n+1);
queue<int>q;
vector<int>a(n+1);
string s;cin>>s;
for(int i=0;i<s.size();i++)tag[i+1]=s[i]-'0';
for(int i=1;i<=n;i++){
cin>>a[i];
idg[a[i]]++;
}
for(int i=1;i<=n;i++){
if(idg[i]==0)q.push(i);
}
vector<int>ans;
while(q.size()){
int x=q.front();q.pop();
int y=a[x];
idg[y]--;
if(tag[x])tag[y]=!tag[y];
if(idg[y]==0)q.push(y);
if(tag[x]){
tag[x]=0;
ans.push_back(x);
}
}
for(int i=1;i<=n;i++){
if(idg[i]&&tag[i]){
vector<int>ansa,ans_b;
int nw=i;
bool flag=false;
do{
idg[nw]--;
if(tag[nw])flag=!flag;
if(flag)ansa.push_back(nw);
else ans_b.push_back(nw);
nw=a[nw];
}while(idg[nw]);
if(flag){
cout<<-1<<'\n';return ;
}
if(ansa.size()<ans_b.size())ans.insert(ans.end(),ansa.begin(),ansa.end());
else ans.insert(ans.end(),ans_b.begin(),ans_b.end());
}
}
cout<<ans.size()<<'\n';
for(int e:ans)cout<<e<<' ';
cout<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
标签:int,Codeforces,flag,tag,push,idg,Div,913,nw
From: https://www.cnblogs.com/wwww-/p/18009608