CF1545C AquaMoon and Permutations 题解

发布时间 2024-01-02 22:48:59作者: Farmer_D

题目链接

点击打开链接

题目解法

很不错的题

首先题目保证了一定有解,所以不用考虑奇怪的无解情况
从列中的数字种类入手
如果一列中有数字 \(c\) 恰好只有第 \(x\) 行存在,那么第 \(x\) 行一定在答案序列中
考虑选了第 \(x\) 行会牵连一些行不能选,那么把这些行去掉,继续跑上面的操作

最后得到的序列一定是:对于每一列,出现过的数一定出现在恰好 \(2\) 行中
为什么?首先出现的数的种类是 行数\(/2\),根据鸽巢原理,如果有一个数出现 \(>2\) 次,那么一定有至少一个数出现次数 \(=1\)

所以限制变成了这两行中只能选一行,不难想到建图,即在这两行之间连边,然后做二分图染色(保证合法)
因为每个联通块染了 \(1\) 个点之后整个块的颜色就固定了,所以方案数为 \(2^{联通块数}\),构造就染色时取 \(color=1\) 的行就可以了

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
const int N=1100,P=998244353;
vector<int> G[N];
int n,a[N][N],col[N];
bool tag[N],cho[N];
void cover(int x){
    tag[x]=true;
    if(col[x]) cho[x]=true;
    for(int y:G[x]) if(!tag[y]) col[y]=col[x]^1,cover(y);else assert(col[y]!=col[x]);
}
void clr(){
    F(i,1,n<<1) cho[i]=tag[i]=false;
    F(i,1,n<<1) G[i].clear();
}
void add_edge(int x,int y){ G[x].pb(y),G[y].pb(x);}
void del(int x){
    F(i,1,n<<1){
        if(tag[i]) continue;
        F(j,1,n) if(a[x][j]==a[i][j]){ tag[i]=true;break;}
    }
}
int sum[N],id[N],id2[N];
void work(){
    n=read();
    F(i,1,n<<1) F(j,1,n) a[i][j]=read();
    while(true){
        int x=-1;
        F(j,1,n){
            F(i,1,n) sum[i]=0;
            F(i,1,n<<1) if(!tag[i]) sum[a[i][j]]++,id[a[i][j]]=i;
            F(i,1,n) if(sum[i]==1){ x=id[i];break;}
            if(x!=-1) break;
        }
        if(x==-1) break;
        cho[x]=true,del(x);
    }
    F(j,1,n){
        F(i,1,n) id[i]=id2[i]=-1;
        F(i,1,n<<1) if(!tag[i]){
            if(id[a[i][j]]==-1) id[a[i][j]]=i;
            else assert(id2[a[i][j]]==-1),id2[a[i][j]]=i;
        }
        F(i,1,n) if(id[i]!=-1) assert(id2[i]!=-1),add_edge(id[i],id2[i]);
    }
    int cnt=1;
    F(i,1,n<<1) if(!tag[i]) cnt=cnt*2%P,cover(i);
    printf("%d\n",cnt);
    F(i,1,n<<1) if(cho[i]) printf("%d ",i);puts("");
    clr();
}
int main(){
    int T=read();
    while(T--) work();
    return 0;
}