2200专项:E. Side Transmutations(字符串翻转 计数问题)

原题: http://codeforces.com/problemset/problem/1065/E

题意:

n长字符串,m长b数组,有k种字符。两个字符串相同当其中一个经过一系列操作变成另外一个。操作为:现在任意 b i b_i bi,翻转前 b i b_i bi个字符,翻转后 b i b_i bi个字符,再交换前后两个 b i b_i bi串。

解析:

听说有Polya计数做法?

我开始的做法是,首先记 a n s = k n ans=k^n ans=kn,再一个一个删除重复的。但是感觉好麻烦。

其实每一个段都是独立的:例如题目中给出b数组为{1,3,6},那么1-1,2-3,4-6三段就是独立的。因为可以通过多次翻转来实现某一个单独段的翻转。

所以,两种结果重复当且仅当有一个段前后不同且交换后两个段相同,例如: x . . y = y . . x x..y=y..x x..y=y..x。所以只需要分开算每一段再乘起来就行。

算一段时,每一段有 k l e n k^{len} klen种情况,前后不同的情况为 k l e n ∗ ( k l e n − 1 ) k^{len}*(k^{len}-1) klen(klen1),这个部分因为一半重复所以除以2。前后相同为 k l e n k^{len} klen,直接加入即可。

最后还要乘以中间段不可交换的可能数量: k n − 2 b m k^{n-2b_m} kn2bm

#include
using namespace std;
#define LL long long
#define debug(i) printf("->%lld\n",i);
const LL mod=998244353 ;

LL n,m,k;
LL Pow(LL a,LL b){
    LL res=1ll;
    while(b){
        if(b&1ll)
            res=res*a%mod;
        b>>=1;a=a*a%mod;
    }
    return res;
}
LL count(LL b){//长度为b的可能性
    return Pow(k,b);
}
LL inv2=Pow(2ll,mod-2);
int main(){
    scanf("%lld%lld%lld",&n,&m,&k);
    LL ans=1ll;
    LL b=0ll;
    for(int i=1;i<=m;i++){
        LL b1;scanf("%lld",&b1);
        ans=ans*(count(b1-b)*(count(b1-b)-1ll)%mod*inv2%mod + count(b1-b) %mod)%mod;
        b=b1;
    }
    ans=ans*count(n-b-b)%mod;
    printf("%lld\n",(ans%mod+mod)%mod);
}

你可能感兴趣的:(想法题)