1. 程式人生 > >2018秦皇島ccpc-camp Steins;Gate (原根+FFT)

2018秦皇島ccpc-camp Steins;Gate (原根+FFT)

con else include complex otp sca max 原根 問題

因為給定的模數P保證是素數,所以P一定有原根.
根據原根的性質,若\(g\)\(P\)的原根,則\(g^k\)能夠生成\([1,P-1]\)中所有的數,這樣的k一共有P-2個.
\(a_i*a_j(mod\ P)=a_k\) 就可以轉化為\(g^i*g^j(mod\ P) = g^{i+j}(mod\ P)=g^k\).
問題轉化為了求有多少對有序的<i,j>滿足 \((i+j)(mod\ (P-1)) = k\).
求出原根後,對\([1,P-1]\)中的每個數編號, 統計每個編號出現的次數,然後FFT求卷積
要特判0,因為原根不會生成0.所以用總的有序對數-其他不含0的有序對數得到含0的有序對,這是0的答案;超過P-1的數肯定沒有符合的有序對.

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
bool notpri[maxn];
int pri[maxn],zyz[maxn];
typedef long long LL;

void pre(int N){
    notpri[1]=1;
    for(int i=2;i<N;++i){
        if(!notpri[i]){
            pri[++pri[0]]=i;
        }
        for(int j=1;j<=pri[0] && (LL)i*(LL)pri[j]<N;++j){
            notpri[i*pri[j]]=1;
            if(i%pri[j]==0){
                break;
            }
        }
    }
}
LL Quick_Pow(LL x,LL p,LL mod){
    if(!p){
        return 1ll;
    }
    LL res=Quick_Pow(x,p>>1,mod);
    res=res*res%mod;
    if((p&1ll)==1ll){
        res=(x%mod*res)%mod;
    }
    return res;
}
int FindRoot(int x){/*求素奇數的最小原根,倘若x不是奇數,但是也有原根的話,將質
因子分解改成對phi(x)即可。倘若要求多個原根,直接接著暴力驗證即可*/
    int tmp=x-1;
    for(int i=1;tmp && i<=pri[0];++i){
        if(tmp%pri[i]==0){
            zyz[++zyz[0]]=pri[i];
            while(tmp%pri[i]==0){
                tmp/=pri[i];
            }
        }
    }
    for(int g=2;g<=x-1;++g){
        bool flag=1;
        for(int i=1;i<=zyz[0];++i){
            if(Quick_Pow((LL)g,(LL)((x-1)/zyz[i]),(LL)x)==1){
                flag=0;
                break;
            }
        }
        if(flag){
            return g;
        }
    }
    return 0;
}

const int MAXN = 4e5 + 10;
const double PI = acos(-1.0);
struct Complex{
    double x, y;
    inline Complex operator+(const Complex b) const {
        return (Complex){x +b.x,y + b.y};
    }
    inline Complex operator-(const Complex b) const {
        return (Complex){x -b.x,y - b.y};
    }
    inline Complex operator*(const Complex b) const {
        return (Complex){x *b.x -y * b.y,x * b.y + y * b.x};
    }
} va[MAXN * 2 + MAXN / 2], vb[MAXN * 2 + MAXN / 2];
int lenth = 1, rev[MAXN * 2 + MAXN / 2];
int N, M;   // f 和 g 的數量
    //f g和 的系數
    // 卷積結果
    // 大數乘積
int f[MAXN],g[MAXN];
vector<LL> conv;
vector<LL> multi;
//f g
void init()
{
    int tim = 0;
    lenth = 1;
    conv.clear(), multi.clear();
    memset(va, 0, sizeof va);
    memset(vb, 0, sizeof vb);
    while (lenth <= N + M - 2)
        lenth <<= 1, tim++;
    for (int i = 0; i < lenth; i++)
        rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (tim - 1));
}
void FFT(Complex *A, const int fla)
{
    for (int i = 0; i < lenth; i++){
        if (i < rev[i]){
            swap(A[i], A[rev[i]]);
        }
    }
    for (int i = 1; i < lenth; i <<= 1){
        const Complex w = (Complex){cos(PI / i), fla * sin(PI / i)};
        for (int j = 0; j < lenth; j += (i << 1)){
            Complex K = (Complex){1, 0};
            for (int k = 0; k < i; k++, K = K * w){
                const Complex x = A[j + k], y = K * A[j + k + i];
                A[j + k] = x + y;
                A[j + k + i] = x - y;
            }
        }
    }
}
void getConv(){             //求多項式
    init();
    for (int i = 0; i < N; i++)
        va[i].x = f[i];
    for (int i = 0; i < M; i++)
        vb[i].x = g[i];
    FFT(va, 1), FFT(vb, 1);
    for (int i = 0; i < lenth; i++)
        va[i] = va[i] * vb[i];
    FFT(va, -1);
    for (int i = 0; i <= N + M - 2; i++)
        conv.push_back((LL)(va[i].x / lenth + 0.5));
}

LL vz[MAXN];
int id[MAXN];
int cnt[MAXN];
int rnk[MAXN];
LL ans[MAXN];

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    pre(maxn-1);
    int n,P ;
    scanf("%d %d",&n, &P);
    int rt = FindRoot(P);
    memset(id,0,sizeof(id));


    LL idx = 1;
    for(int i=0;i<P-1;++i){         //一共只有[0,P-2],P-1個id
        id[idx] = i;
        rnk[i]= idx;
        idx = idx*rt%P;
    }

    for(int i=1;i<=n;++i){
        LL tmp;
        scanf("%lld",&tmp);
        vz[i] = tmp;
        tmp%=P;
        if(tmp==0) continue;        //卷積中不考慮0的貢獻
        cnt[id[tmp]]++;
    }

    N = M = P;
    for(int i=0;i<P-1;++i){
        f[i] = g[i] = cnt[i];
    }

    getConv();
    int sz = conv.size();

    for(int i=0;i<sz;++i){
        LL tmp = conv[i];
        ans[rnk[i%(P-1)]] += tmp;
    }

    LL tot = (LL)n*n;           //全部的枚舉可能-不選0之外的組合 = 包含0的組合
    for(int i=1;i<P;++i){
        tot -= ans[i];
    }
    ans[0] = tot;
    for(int i=1;i<=n;++i){
        if(vz[i]>=P) printf("0\n");
        else{
            printf("%lld\n",ans[vz[i]]);
        }
    }
    return 0;
}

2018秦皇島ccpc-camp Steins;Gate (原根+FFT)