UVA12387 Alphabet Soup [Polya + KMP]

Alphabet SoupAlphabet Soup


Descriptionmathcal{Description}
在一个圆中, 一共 360,000360,000 个角度, 初始有PP个物品, 分别在PP个不同的角度, SS 个颜色去染这 PP 个物品, 求方案数量.
旋转同构 .


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


实现部分

  • 将所有物品的角度排序, 进行差分操作
  • 数组拷贝一次放在后方
  • 原始差分数组作为模式串, 倍长数组作为文本串, 进行 KMPKMP 字符串匹配
  • 若匹配成功, 说明旋转若干角度可以与原来重合, 计入 置换总数 G|G| , Ans+=SGcd(P,k)Ans+=S^{Gcd(P,k)}
    其中kk为旋转多少度 .
  • 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
", 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
", Ans);
}

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

原文地址:https://www.cnblogs.com/zbr162/p/11822576.html