[图论记录]AT2267 [AGC008E] Next or Nextnext

command_block

2021-04-26 20:51:15

Personal

**题意** : 给定一个长度为 $n$ 的 序列 $a$ ,问有多少长度为 $n$ 的排列 $p$ ,满足对于任意 $i$ 有 $p_i=a_i$ 或 $p_{p_i}=a_i$。 答案对 $10^9+7$ 取模。 $n\leq 10^5$ ,时限$\texttt{2s}$。 ------------ 根据排列 $p$ ,对每个 $i$ 连边 $i\rightarrow p_i$ ,会形成若干个有向环。 约束相当于 : 在 $u$ 点两步数以内要能走到 $a_u$。 根据序列 $a$ ,对每个 $i$ 连边 $i\rightarrow a_i$ ,会形成基环内向树森林。 先观察一个 $p$ 图可能对应怎样的 $a$ 图 : - 情况 $1$ : 全部有 $p_i=a_i$ 会形成一个和原来相同的环。 - 情况 $2$ : 全部有 $p_{p_i}=a_i$ 若环长为奇数,会形成一整个环,但和原来不同。 若环长为偶数,会形成两个等大的小环。 ![](https://cdn.luogu.com.cn/upload/image_hosting/u29jbc1y.png?x-oss-process=image/resize,m_lfit,w_360) - 情况 $3$ : 部分有 $p_i=a_i$ ,部分有 $p_{p_i}=a_i$ ![](https://cdn.luogu.com.cn/upload/image_hosting/hw0y61ca.png?x-oss-process=image/resize,m_lfit,w_480) 不难发现,会得到一个特殊的基环树,环上挂着几条链。 接下来考虑反映射,即由 $a$ 图得到 $p$ 图。 - **环** 记 $a$ 中大小为 $i$ 的环的个数为 $c$ ,考虑这些环组合的方案。 枚举配对的环的个数 $2k$。 - 从 $c$ 个环中选出 $2k$ 个 : $\binom{c}{2k}$。 - 将 $2k$ 个环配对 : $\dfrac{(2k)!}{k!2^k}$。 - 每对环有 $i$ 种拼法(旋转) : $i^j$ - 当 $i$ 为奇数且 $i>1$ 时,落单的环可以生成两个不同的环 : $2^{c-2j}$ - **基环树** 要把链塞入环的缝隙中。 ![](https://cdn.luogu.com.cn/upload/image_hosting/2vbo4bf2.png?x-oss-process=image/resize,m_lfit,w_360) $a$ 是一段外链的长度(不包括在环上的链头),$b$ 是该链头到下一个链头之间的边数。 手玩可以发现,若 $a>b$ 则方案数为 $0$ ,若 $a=b$ 方案为 $1$ ,若 $a<b$ 方案为 $2$。 ![](https://cdn.luogu.com.cn/upload/image_hosting/5lirq5fy.png?x-oss-process=image/resize,m_lfit,w_420) 利用乘法原理计算答案即可。 咋这么难写…… ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 100500 using namespace std; const int mod=1000000007,inv2=500000004; struct UFS{ int f[MaxN]; void Init(int n) {for (int i=1;i<=n;i++)f[i]=i;} int find(int u) {return f[u]==u ? u : f[u]=find(f[u]);} bool merge(int u,int v){ u=find(u);v=find(v); if (u==v)return 0; f[u]=v;return 1; } }T; vector<int> g[MaxN]; int st[MaxN],tot,fl; void pfs(int u) { st[++tot]=u; if (st[tot]==fl)return ; for (int i=0;i<g[u].size();i++){ pfs(g[u][i]); if (st[tot]==fl)return ; }tot--; } bool vis[MaxN]; int buf,ans=1; void dfs(int u) { vis[u]=1;buf++; int c=0; for (int i=0;i<g[u].size();i++) if (!vis[g[u][i]]){ dfs(g[u][i]); c++; } if (c>1)ans=0; } int o[MaxN]; void solve(int u,int v) { tot=0;fl=v;pfs(u); reverse(st+1,st+tot+1); for (int i=1;i<=tot;i++)vis[st[i]]=1; int l1=tot;while(l1>=1&&g[st[l1]].size()+(l1==1)==1)l1--; if (l1==0){o[tot]++;return ;} for (int i=1,las=l1-tot;i<=tot;i++) if (g[st[i]].size()+(i==1)>1){ int c2=i-las;buf=-1;dfs(st[i]); ans=(ans*((buf<c2)+(buf<=c2)))%mod; las=i; } } ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } int fac[MaxN],ifac[MaxN],pw2[MaxN],ipw2[MaxN]; ll C(int n,int m) {return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;} void Init(int n) { fac[0]=pw2[0]=ipw2[0]=1; for (int i=1;i<=n;i++){ fac[i]=1ll*fac[i-1]*i%mod; pw2[i]=(pw2[i-1]<<1)%mod; ipw2[i]=1ll*ipw2[i-1]*inv2%mod; }ifac[n]=powM(fac[n]); for (int i=n;i;i--) ifac[i-1]=1ll*ifac[i]*i%mod; } struct Data{int u,v;}b[MaxN]; int n,m; int main() { scanf("%d",&n); T.Init(n);Init(n); for (int i=1;i<=n;i++)pw2[i]=(pw2[i-1]<<1)%mod; for (int i=1,p;i<=n;i++){ scanf("%d",&p); if (T.merge(i,p))g[p].push_back(i); else b[++m]=(Data){i,p}; }for (int i=1;i<=m;i++)solve(b[i].u,b[i].v); for (int i=1;i<=n;i++){ if (!o[i])continue; int c=o[i],buf=0,pw=1; for (int k=0;2*k<=c;k++){ int sav=C(c,2*k)*fac[2*k]%mod*ifac[k]%mod*ipw2[k]%mod*pw%mod; if ((i&1)&&i>1)sav=1ll*sav*pw2[c-2*k]%mod; buf=(buf+sav)%mod; pw=1ll*pw*i%mod; }ans=1ll*ans*buf%mod; }printf("%d",ans); return 0; } ```