专栏文章
AGC008E Next or Nextnext 题解
AT_agc008_e题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miniowpd
- 此快照首次捕获于
- 2025/12/02 03:03 3 个月前
- 此快照最后确认于
- 2025/12/02 03:03 3 个月前
设从 指向 所形成的图为 ,从 指向 所形成的图为 ,考虑考查 和 之间的关系。
显然 由若干个环组成,于是考虑 中的每一个环:
- 若环上的每一个点都满足 ,则 中有一个一模一样的环;
- 若环上的每一个点都满足 且环长为奇数,则 中有一个环长相同但不一样的环;
- 若环上的每一个点都满足 且环长为偶数,则 中有两个环长为该环长度一半的环;
- 若环上既存在满足 的点又存在满足 的点,则 中会存在一棵由一个环和若干条链组成的基环内向树。
接下来考虑 如何推回 。
先考虑 中的环。设 中有 个长度为 的环,枚举要合并的环的数量 :
- 若 :
- 从 个环中选出 个环,方案数为 ;
- 从 个环中选出左侧的 个环,钦定左侧的环的顺序为 ,枚举右侧环的顺序,并去掉交换同一行两个环的方案,得到方案数为 ;
- 每对环有 种合并方式,方案数为 ;
- 若 为奇数且 ,则每个不被合并的环也有 种方案,方案数为 ;
将每个步骤的方案数相乘即可得到这个部分的结果,将每个 的结果相加即可得到每种环长的答案。
再考虑 中的基环内向树。不妨设基环内向树的环上的点依次为 ,其中对于每个不大于 的正整数 都存在一条从 指向 的边;设挂着链的点依次为 ,挂在 上的链的长度为 ,则我们需要将 插回 到 之间。设 到 之间有 条边:
- 若 ,则插回的方案数为 ;
- 若 ,则插回的方案数为 ;
- 若 ,则插回的方案数为 。
将每条链插回的方案数相乘即可得到这部分的结果,答案即为每个环和每棵基环内向树的方案数的乘积。
朴素实现的时间复杂度为 ,精细实现可以做到 。
Cconst int N=1e5+5,mod=1e9+7;
int n,a[N],c[N],vis[N],ans=1,fac[N<<1],infac[N<<1];
vector <int> ve[N];
void add(int &a,int b){
a+=b;
if(a>=mod) a-=mod;
}
int ad(int a,int b){
a+=b;
if(a>=mod) a-=mod;
return a;
}
int ksm(int a,int b){
int res=1;
while(b){
if(b&1) res=1ll*res*a%mod;
b>>=1,a=1ll*a*a%mod;
}
return res;
}
int C(int n,int m){
return 1ll*fac[n]*infac[m]%mod*infac[n-m]%mod;
}
void dfs(int u,bool f,int len){
f|=(ve[u].size()!=1);
vis[u]=1,len++;
if(vis[a[u]]){
if(!f) return c[len]++,void();
vector <int> s,p,l;
s.pb(u),vis[u]=2;
int v=a[u];
while(v!=u) s.pb(v),vis[v]=2,v=a[v];
int m=s.size();
for(int i=0;i<m;i++){
int u=s[i];
if(ve[u].size()==2){
if(vis[ve[u][0]]==2) v=ve[u][1];
else v=ve[u][0];
int cnt=0;
while(1){
vis[v]=1,cnt++;
if(ve[v].size()==2) cout<<0<<endl,exit(0);
if(ve[v].size()==0) break;
v=ve[v][0];
}
p.pb(i),l.pb(cnt);
}
}
int k=p.size();
for(int i=0;i<k;i++){
int e;
if(i==0) e=p[i]+m-p[k-1];
else e=p[i]-p[i-1];
if(e>l[i]) add(ans,ans);
if(e<l[i]) cout<<0<<endl,exit(0);
}
return;
}
dfs(a[u],f,len);
}
void solve(){
cin>>n;
fac[0]=infac[0]=1;
for(int i=1;i<=n+n;i++) fac[i]=1ll*i*fac[i-1]%mod;
infac[n+n]=ksm(fac[n+n],mod-2);
for(int i=n+n-1;i>0;i--) infac[i]=1ll*(i+1)*infac[i+1]%mod;
for(int i=1;i<=n;i++) cin>>a[i],ve[a[i]].pb(i);
for(int i=1;i<=n;i++) if(ve[i].size()>2) cout<<0<<endl,exit(0);
for(int i=1;i<=n;i++){
if(vis[i]) continue;
dfs(i,0,0);
}
for(int i=1;i<=n;i++){
int sum=0;
for(int j=0;j+j<=c[i];j++){
int res=1;
if(j!=0) res=1ll*C(c[i],j+j)*C(j+j,j)%mod*fac[j]%mod*ksm((mod+1)/2,j)%mod*ksm(i,j)%mod;
if((i&1)&&i!=1) res=1ll*res*ksm(2,c[i]-j-j)%mod;
add(sum,res);
}
ans=1ll*ans*sum%mod;
}
cout<<ans<<endl;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...