「PA2022」Medrcy

zyxawa發表於2024-07-28

\(f_i\) 表示第 \(i\) 個人知道的咒語集合,\(c_i\) 為其補集,那麼第 \(i\) 個人第 \(k\) 天會離開當且僅當存在一個序列 \(a_{1\sim k-1}\),使得 \(\bigcup\limits_{j=1}^{k-1}(f_i \cup f_{a_j})=\varnothing\),即 \(\bigcap_{j=1}^{k-1}(c_i \cap c_{a_j})=\text{U}\)

考慮連線 \((a_i,b_i)\),那麼第一次有人沒來聚會的天數為最小點覆蓋數 \(k\)(選取最少的點使得它們能覆蓋圖中所有的邊),第 \(i\) 個人第 \(k\) 天會離開當且僅當存在一種最小點覆蓋方案使得選擇了節點 \(i\),注意到如果不選一個點就一定會選擇它的所有鄰居,搜尋時可以優先選擇度數最大的點進行操作,若此點度數不大於 \(2\) 而會使時間複雜度錯誤,但此時原圖一定由若干環或鏈組成,根據奇偶性直接判斷即可,複雜度為 \(T(k)=T(k-1)+T(k-3) \approx 10^5\)

#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,a,b,sum,cnt,ans[1001],vis[1001],use[1001],aus[1001];
set <int> G[1001];
basic_string <int> S;
void loop(int x){
    use[x]=1,S+=x;
    for(int y:G[x]) if(!use[y]) loop(y);
}
void dfs(int v){
    if(v>k) return;
    int d=1,w=0;
    for(int i=1;i<=n;i++) if(G[i].size()>G[d].size()) d=i;
    if(G[d].size()<=2){
        int now=v;
        for(int i=1;i<=n;i++) use[i]=0,aus[i]=vis[i];
        for(int i=1;i<=n;i++){
            if(!use[i]&&G[i].size()==1){
                S.clear(),loop(i),now+=S.size()/2;
                if(S.size()&1) for(int j=1;j<S.size()-1;j+=2) aus[S[j]]=1;
                else for(int j:S) aus[j]=1;
            }
        }
        for(int i=1;i<=n;i++){
            if(!use[i]&&G[i].size()>1){
                S.clear(),loop(i),now+=(S.size()+1)/2;
                for(int j:S) aus[j]=1;
            }
        }
        if(now<=k){
            if(now<sum) sum=now,memset(ans,0,sizeof(ans));
            if(now==sum) for(int i=1;i<=n;i++) ans[i]|=aus[i];
        }
        return;
    }
    vector <pair<int,int>> now;
    for(int i:G[d]) now.push_back({i,d}),G[i].erase(d);
    G[d].clear(),vis[d]=1,dfs(v+1),vis[d]=0;
    for(auto [u,v]:now) G[u].insert(v),G[v].insert(u);
    now.clear(),w=G[d].size(),S.clear();
    for(int i:G[d]) S+=i;
    for(int i:S) for(int j:G[i]) now.push_back({i,j}),G[j].erase(i);
    for(int i:S) vis[i]=1,G[i].clear();
    dfs(v+w);
    for(auto [u,v]:now) G[u].insert(v),G[v].insert(u);
    for(int i:G[d]) vis[i]=0;
}
int main(){
	scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&k),cnt=0,sum=1e9;
        for(int i=1;i<=n;i++) G[i].clear(),ans[i]=0;
        for(int i=1;i<=m;i++) scanf("%d%d",&a,&b),G[a].insert(b),G[b].insert(a);
        dfs(0);
        if(sum==1e9) printf("-1\n");
        else{
            for(int i=1;i<=n;i++) cnt+=ans[i];
            printf("%d %d\n",sum,cnt);
            for(int i=1;i<=n;i++) if(ans[i]) printf("%d ",i);
            printf("\n");
        }
    }
    return 0;
}