UVA12387 Alphabet Soup [Polya + KMP]

A l p h a b e t   S o u p Alphabet\ Soup Alphabet Soup


D e s c r i p t i o n \mathcal{Description} Description
在一个圆中, 一共 360 , 000 360,000 360,000 个角度, 初始有 P P P个物品, 分别在 P P P个不同的角度, S S S 个颜色去染这 P P P 个物品, 求方案数量.
旋转同构 .


正 解 部 分 正解部分
这道题目为项链问题的一个拓展,
相比项链问题, 该题的 项链 旋转后不一定与原来重合,
对该问题目标是: 找 出 所 有 旋 转   k   度 后 会 重 合 的 方 案 , 以 统 计 答 案 找出所有旋转\ k\ 度后会重合的方案, 以统计答案  k ,
旋转后使得每个角度重合, 等价于每个角度的 间距 相同, 于是将位置数组转换为间距数组,
进行 环状处理.


实 现 部 分 实现部分

  • 将所有物品的角度排序, 进行差分操作
  • 数组拷贝一次放在后方
  • 原始差分数组作为模式串, 倍长数组作为文本串, 进行 K M P KMP KMP 字符串匹配
  • 若匹配成功, 说明旋转若干角度可以与原来重合, 计入 置换总数 ∣ G ∣ |G| G , A n s + = S G c d ( P , k ) Ans+=S^{Gcd(P,k)} Ans+=SGcd(P,k)
    其中 k k k为旋转多少度 .
  • ∣ G ∣ |G| G, 输出答案.

#include<cstdio>
#include<cctype>
#include<algorithm>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 360005;
const int mod = 100000007;

int S;
int P;
int Ans;
int A[maxn<<1];
int B[maxn];
int nxt[maxn];

int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }

int KSM(int a, int b){
        int s = 1;
        while(b){
                if(b & 1) s = 1ll*s*a % mod;
                a = 1ll*a*a % mod;
                b >>= 1;
        }
        return s;
}

void Get_next(){
        nxt[0] = -1;
        int i = 0, t = -1;
        while(i < P-1){
                if(t == -1 || B[i] == B[t]) nxt[++ i] = ++ t;
                else t = nxt[t];
        }
}

void KMP(){
        int cnt = 0;
        int t_1 = 0, t_2 = 2;
        while(t_2 < (P<<1)){
                if(t_1 == -1 || B[t_1] == A[t_2]) t_1 ++, t_2 ++;
                else t_1 = nxt[t_1];
                if(t_1 == P-1){
                        int k = t_2 - P - 1;
                        Ans = ( 1ll*Ans + KSM(S, Gcd(P, k) ) ) % mod;
                        t_1 = nxt[t_1];
                        cnt ++;
                }
        }
        Ans = 1ll*Ans*KSM(cnt, mod-2) % mod;
}

void Work(){
        for(reg int i = 1; i <= P; i ++) A[i] = read();
        if(P == 1){ printf("%d\n", S); return ; }
        std::sort(A+1, A+P+1);
        for(reg int i = 1; i <= P; i ++) A[i+P] = A[i] + 360000;
        for(reg int i = P<<1; i > 1; i --) A[i] -= A[i-1];
        for(reg int i = 1; i <= P; i ++) B[i-1] = A[i+1];
        Ans = 0;
        Get_next();
        KMP();
        printf("%d\n", Ans);
}

int main(){
        S = read(), P = read();
        while(~S && ~P) Work(), S = read(), P = read();
        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值