Codeforces Round 913 (Div. 3) G.

题目链接

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();
    }
}

posted @ 2024-02-06 13:58  WW爆米花  阅读(5)  评论(0编辑  收藏  举报