bzoj3992 [SDOI2015]序列统计

Description


小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。

对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复

Solution


对于不会做的题可以先从部分分想起,0分暴力也行
先考虑dp,有f[i][j]=f[i1][jinv(k)%mod](kS)f[i][j]=∑f[i−1][j∗inv(k)%mod](k∈S),10分到手
注意到了N的范围进而往矩乘dp想,进而倦生

显然这样会跳进坑里出不来的。引入原根的概念:设原根为g(下同,那么有gimodp≢gjmodp(ij)gimodp≢gjmodp(∀i≠j)。我们求出m的原根,这样所有数都能表示为gx(x[0,m2])gx(x∈[0,m−2]),两数字相乘就变为指数的加法了

一个数x有原根当且仅当x=24pk2pkx=2、4、pk、2pk,其中p是奇素数。原根一般比较小可以直接暴力。将x-1质因数分解得到x1=pikix−1=∏piki后,一个数a是x的原根满足ax1pi≢1(modx)∀ax−1pi≢1(modx)而对于x不为质数时x-1换成φ(x)φ(x)即可

那么有f[i][j]=f[i1][jk]c[k]f[i][j]=∑f[i−1][j−k]∗c[k],这里的c[k]c[k]为1当且仅当gkSgk∈S
这就是熟悉的卷积形式了,用NTT是mlogm的。而卷积满足结合律,可以用快速幂加速

NTT与FFT不同之处在于NTT采用原根带入求点值,这里终于写了非递归版的。一个技巧确认能不能用NTT就是看模数有没有原根get

Code


#include <stdio.h>
#include <string.h>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int N=1048586;
const int MOD=1004535809;

int a[N],b[N],c[N],u[N],ans[N],rev[N];
int n,m,x,s,ny,len;

int ksm(int x,int dep,int mod) {
    int ret=1;
    while (dep) {
        if (dep&1) ret=(LL)ret*x%mod;
        dep/=2; x=(LL)x*x%mod;
    }
    return ret;
}

int get_g(int x) {
    std:: vector <int> v;
    int tmp=x-1;
    rep(i,2,x) {
        if (tmp%i) continue;
        v.push_back(i);
        while (tmp%i==0) tmp/=i;
    }
    rep(i,2,x) {
        bool flag=true;
        rep(j,0,v.size()-1) {
            if (ksm(i,(x-1)/v[j],x)!=1) continue;
            flag=false;
            break;
        }
        if (flag) return i;
    }
}

void NTT(int *a,int f) {
    rep(i,0,len-1) if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
    for (int i=1;i<len;i*=2) {
        int wn;
        if (f==1) wn=ksm(3,(MOD-1)/i/2,MOD);
        else wn=ksm(3,MOD-1-(MOD-1)/i/2,MOD);
        for (int j=0;j<len;j+=i*2) {
            int w=1;
            rep(k,0,i-1) {
                int u=a[j+k],v=(LL)w*a[j+k+i]%MOD;
                a[j+k]=(u+v)%MOD;
                a[j+k+i]=(u-v)%MOD;
                w=(LL)w*wn%MOD;
            }
        }
    }
}

void mul(int *c,int *ta,int *tb) {
    memcpy(a,ta,sizeof(a));
    memcpy(b,tb,sizeof(b));
    NTT(a,1); NTT(b,1);
    rep(i,0,len-1) c[i]=(LL)a[i]*b[i]%MOD;
    NTT(c,-1);
    rep(i,0,len-1) c[i]=(LL)c[i]*ny%MOD;
    rep(i,m-1,len-1) c[i-m+1]=(c[i-m+1]+c[i])%MOD,c[i]=0;
}

void solve(int *c,int dep) {
    while (dep) {
        if (dep&1) mul(ans,ans,c);
        dep/=2; mul(c,c,c);
    }
}

int main(void) {
    scanf("%d%d%d%d",&n,&m,&x,&s);
    int g=get_g(m),lg=0;
    for (len=1;len<=m*2;len*=2,lg++);
    for (int i=0;i<len;i++) rev[i]=(rev[i/2]/2)|((i&1)<<(lg-1));
    for (int i=1,w=g;i<m-1;i++,w=(LL)w*g%m) u[w]=i;
    ny=ksm(len,MOD-2,MOD);
    rep(i,1,s) {
        int x; scanf("%d",&x);
        if (!x) continue;
        c[u[x]]=1;
    }
    ans[0]=1;
    solve(c,n);
    printf("%d\n", (ans[u[x]]+MOD)%MOD);
    return 0;
}
内容概要:本文探讨了在MATLAB/SimuLink环境中进行三相STATCOM(静态同步补偿器)无功补偿的技术方法及其仿真过程。首先介绍了STATCOM作为无功功率补偿装置的工作原理,即通过调节交流电压的幅值和相位来实现对无功功率的有效管理。接着详细描述了在MATLAB/SimuLink平台下构建三相STATCOM仿真模型的具体步骤,包括创建新模型、添加电源和负载、搭建主电路、加入控制模块以及完成整个电路的连接。然后阐述了如何通过对STATCOM输出电压和电流的精确调控达到无功补偿的目的,并展示了具体的仿真结果分析方法,如读取仿真数据、提取关键参数、绘制无功功率变化曲线等。最后指出,这种技术可以显著提升电力系统的稳定性与电能质量,展望了STATCOM在未来的发展潜力。 适合人群:电气工程专业学生、从事电力系统相关工作的技术人员、希望深入了解无功补偿技术的研究人员。 使用场景及目标:适用于想要掌握MATLAB/SimuLink软件操作技能的人群,特别是那些专注于电力电子领域的从业者;旨在帮助他们学会建立复杂的电力系统仿真模型,以便更好地理解STATCOM的工作机制,进而优化实际项目中的无功补偿方案。 其他说明:文中提供的实例代码可以帮助读者直观地了解如何从零开始构建一个完整的三相STATCOM仿真环境,并通过图形化的方式展示无功补偿的效果,便于进一步的学习与研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值