P11563 Sol
CarroT1212 · · 题解
我擦,企鹅罐!生存战略!
挺好的性质题。写详细亿点。
排列可以看成有向环集合来计数。设在
首先由于
(观察样例)发现有个奇怪情况:
于是把二元环换成无向边后
-
- 对于
G_1 中的自环,不用管它,它在G_2 里还是自环。 -
-
别急,还有高手!如果
这种情况要求
没事还可以容斥,我们钦定其中
实现就是看看
const ll J=1e18,N=1e6+7,P=998244353;
ll qp(ll x,ll y=P-2) { return y?(y&1?x:1)*qp(x*x%P,y>>1)%P:1; }
ll fac[N],fnv[N];
struct init { init() {
fac[0]=1; for (ll i=1;i<N;i++) fac[i]=fac[i-1]*i%P;
fnv[N-1]=qp(fac[N-1]); for (ll i=N-1;i;i--) fnv[i-1]=fnv[i]*i%P;
} } A;
ll C(ll x,ll y) { return x<0||y<0||x<y?0:fac[x]*fnv[y]%P*fnv[x-y]%P; }
ll n,a[N],ans,vis[N];
ll c3,c21,c22;
vector<ll> e[N];
void no() { cout<<"0",exit(0); }
void get(ll p) {
if (e[p].size()>1) no();
vis[p]=1;
if (e[p].size()==1&&!vis[e[p][0]]) get(e[p][0]);
}
void mian() {
scanf("%lld",&n);
for (ll i=1;i<=n;i++) scanf("%lld",&a[i]),e[a[i]].pb(i);
for (ll i=1;i<=n;i++) if (!vis[i]) {
if (a[i]==i) {
if (e[i].size()>1) no();
vis[i]=1;
}
else if (a[a[i]]==i) {
ll x=i,y=a[i],flg=0;
vis[x]=vis[y]=1;
if (e[x].size()>2||e[y].size()>2) no();
if (e[x].size()==2) flg=1,get(e[x][0]^e[x][1]^y);
if (e[y].size()==2) flg=1,get(e[y][0]^e[y][1]^x);
flg?c22++:c21++;
}
}
for (ll i=1;i<=n;i++) if (!vis[i]) {
if (!e[i].size()) no();
c3++,get(i);
}
ll ans=0;
for (ll i=0;i<=c21;i++) (ans+=(i&1?P-1:1)*C(c21,i)%P*qp(2,c21+c22-i)%P*fac[c21+c22-i])%=P;
cout<<ans*qp(2,c3)%P;
}