CodeForces - 1065E Side Transmutations (组合数学+Polya定理)

题目链接

可以将左半部分区间划分为[1,b1],[b1+1,b2],[b2+1,b3]...若干个小段,每一段都可以和右半部分对称位置上的做交换。

根据Burnside引理,等价类的个数等于所有置换不动点个数的平均值。又根据Polya定理,置换f不动点的个数等于$A^{m(f)}$,A为颜色数,m(f)为f的循环节个数

假如一段长度为k区间可以和对面互换,那么该置换的循环节个数为n-k(左右两边对称的k个必须涂成一样颜色,其余的n-2k个任意涂色),不动点个数即为$A^{n-k}$

对b数组从小到大排序,设c[i]=b[i]-b[i-1],由于若干个小段可以拼成一个长度为这些小段之和的大段,最终结果为$\frac{A^{n}+A^{n-c[1]}+A^{n-c[2]}+A^{n-(c[1]+c[2])}+...}{2^m}$,写成乘积的形式即为$\frac{A^n(1+A^{-c[1]})(1+A^{-c[2]})...}{2^m}$,可以在$O(m(logm+logn))$的时间内求出

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=2e5+10,mod=998244353,inf=0x3f3f3f3f;
 5 int n,m,A,b[N];
 6 int Pow(int x,int p) {
 7     int ret=1;
 8     for(; p; p>>=1,x=(ll)x*x%mod)if(p&1)ret=(ll)ret*x%mod;
 9     return ret;
10 }
11 int main() {
12     scanf("%d%d%d",&n,&m,&A);
13     for(int i=0; i<m; ++i)scanf("%d",&b[i]);
14     sort(b,b+m);
15     for(int i=m; i>0; --i)b[i]-=b[i-1];
16     int ans=1;
17     for(int i=0; i<m; ++i)ans=(ll)ans*(1+Pow(A,mod-1-b[i]))%mod;
18     ans=(ll)ans*Pow(A,n)%mod*Pow(2,mod-1-m)%mod;
19     printf("%d\n",ans);
20     return 0;
21 }

 

posted @ 2020-03-11 14:06  jrltx  阅读(263)  评论(0编辑  收藏  举报