(乱搞艹爆正解系列)

对不起,由于博主太弱了,并不会正解的多项式exp(甚至多项式exp我都不会2333)。

只能来说一说我是怎么乱搞的啦QWQ

首先这个题最关键的性质是: 一个在原置换 g 中长度为 l 的环,在 f 中会被拆成 gcd(l,n) 个 长度为 l/gcd(l,n) 的环  [可以类比同余系中的一些变换]

所以知道了这个,我们怎么反着搞回去呢???

先 O(N) 找出所有循环的长度,然后记 num[i] 为 f 中长度为i的环有多少个。这个时候,我们再知道了 哪些长度一样的环被合并到了一起,就可以计算出答案了。

因为长度不一样的环之间互不影响,所以我们可以把每个长度的环的答案乘起来,就是最终答案了(如果f中没有长度为i的环,那么这个长度的环的答案是1)。

于是我们就可以先从1到n扫一遍,把每个 i 存到 i/gcd(i,n) 的 vector 中去,表示 gcd(i,n) 个 i/gcd(i,n) 可以合并成一个 长度为i的环。

然后对于每个长度,我们只需要扫它的倍数dp即可,复杂度O(玄学)。

(至于中间怎么推dp的式子就不说了,就是一些组合数学的基本功练习2333)

为什么是 O(玄学)呢?

单纯的扫一遍倍数已经是 O(N * log (N))了,但是因为很多地方可以不用扫(比如没出现的环长,或者一种环长的环的长度和很小,不需要用到很大的dp数组),仔细算一下,扫的过程其实是 O(图中环的个数) 的,已经是低于线性的了。

但是可能一个vector里面会有很多元素啊,这样dp的时候会不会炸啊???

就比如 i=1 的时候,我们可能要扫 1~n中所有数的dp数组,再乘一个 vector 的大小,难道不是秒变 O(N^2)了吗?

但可以发现此时vector中的元素只有 n 的约数(请自行yy为什么),1e5以下的约数最多的到100吗(不太清楚)??

所以就算用心卡我,也很难卡掉吧QWQ

(达成目标 : 在代码量是标程 1/3的情况下 速度 比标程快了8倍  2333)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define pb push_back
const int maxn=100005,ha=998244353;
inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
inline int ksm(int x,int y){
int an=1;
for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
return an;
} vector<int> g[maxn],w[maxn];
int n,a[maxn],num[maxn],ans=1;
int jc[maxn],ni[maxn],f[maxn];
bool v[maxn]; int gcd(int x,int y){ return y?gcd(y,x%y):x;} inline int P(int x,int y){ return jc[x]*(ll)ni[x-y]%ha;} inline int work(int x){
int an=0;
while(!v[x]) an++,v[x]=1,x=a[x];
return an;
} inline void solve(){
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*(ll)i%ha;
ni[n]=ksm(jc[n],ha-2);
for(int i=n;i;i--) ni[i-1]=ni[i]*(ll)i%ha; for(int i=1;i<=n;i++) if(!v[i]) num[work(i)]++; for(int i=n;i;i--) g[i/gcd(i,n)].pb(i); for(int i=1;i<=n;i++)
for(int j=0;j<g[i].size();j++) w[i].pb(ksm(i,g[i][j]/i-1)); for(int i=1;i<=n;i++) if(num[i]){
int T=num[i]*i,now;
f[0]=1; for(int j=i;j<=T;j+=i){
f[j]=0;
for(int l=g[i].size()-1;l>=0;l--){
now=g[i][l];
if(now>j) break; ADD(f[j],f[j-now]*(ll)P(j/i-1,now/i-1)%ha*(ll)w[i][l]%ha);
}
} ans=ans*(ll)f[T]%ha;
// cout<<f[T]<<endl;
}
} int main(){
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout); scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i); solve(); printf("%d\n",ans);
return 0;
}

  

最新文章

  1. VMware Data Recovery备份恢复vmware虚拟机
  2. IE中使用jquery的fadeIn()失效的问题
  3. 根据.MDF文件查看 SQL数据库的版本信息
  4. OpenCV白平衡算法之灰度世界法(消除RGB受光照影响)
  5. 3640: JC的小苹果 - BZOJ
  6. 将yyyyMMdd HH:mm:ss格式的时间转换成时间类型
  7. 前端面试题第二波,要offer的看过来~
  8. shell中的环境变量
  9. localhost简介、localhost与 127.0.0.1 及 本机IP 的区别
  10. MongoDB分片 在部署和维护管理 中常见事项的总结
  11. 咸鱼入门到放弃6--jsp&lt;一&gt;三指令
  12. 树的子结构(JAVA)
  13. python pip安装找不到指定包的时候怎么解决
  14. 第二届强网杯wp
  15. Nginx 关闭防火墙
  16. SQLite限定行数
  17. 基本的sqlplus命令
  18. 项目冲刺Forth
  19. Android Launcher分析和修改6——页面滑动(PagedView)
  20. 在数组中寻找和为定值的n个数

热门文章

  1. Codeforces Round #515 (Div. 3) E. Binary Numbers AND Sum
  2. HDU2732:Leapin&#39; Lizards(最大流)
  3. ubunut14.04 mentohust配置
  4. JavaScript中Array(数组)的属性和方法(转)
  5. 创建 React 项目
  6. centos yum 安装 mysql
  7. 转:Spring AOP 注解方式实现的一些“坑”
  8. finally return 执行顺序问题
  9. idea控制台中文乱码问题解决办法
  10. php session 阻塞 过期不自动清除session文件