zoukankan      html  css  js  c++  java
  • Luogu3321 [SDOI2015]序列统计

    Description

    link

    给定一个集合 (s),和整数 (x,n,m)(nle 10^9 mle 8000 x le m-1)

    保证 (forall ain {S},ale m)

    每次从集合里面随机选数,一共选 (n) 次,求选出来的数的乘积 (equiv x (mod m)) 的方案数

    Solution

    上手一个比较暴力的 (dp)

    (f_{i,j}) 是长度为 (i) 的序列,乘积模数为 (j) 的方案数

    转移枚举每个可以被随机出来的数转移就好了

    复杂度显然爆炸

    $N le 10^9 ? $ 矩阵乘法?

    然而并不会……


    有一个比较好的优化:

    [f_{2 imes i,p}=sum_{x imes yequiv p(mod m)} f_{i,x} imes f_{i,y} ]

    乘法取模并不能计算,怎么把乘法变加法?

    (log) 用原根,即:

    [f_{2 imes i,log_{g} p}=sum_{log_{g}x \%m+log _ {g} y\%m=log_{g} p\% m } f_{i,log_{g}x} imes f_{i,log_{g}y} ]

    直接 (NTT) 卷上 (log (n)) 次即可

    Code

    #include<bits/stdc++.h>
    using namespace std;
    //#define int long long
    namespace yspm{
    	inline int read()
    	{
    		int res=0,f=1; char k;
    		while(!isdigit(k=getchar())) if(k=='-') f=-1;
    		while(isdigit(k)) res=res*10+k-'0',k=getchar();
    		return res*f;
    	}
    	const int mod=1004535809,N=50010,L=8010;
    	int g,d[100],cnt,n,m,x,sz,s[L],id[L];
    	inline int ksm1(int x,int y,int mod)
    	{
    		int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod;
    		return res;
    	}
    	inline int ksm(int x,int y)
    	{
    		int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod;
    		return res;
    	}
    	inline bool check(int x,int p)
    	{
    		for(int i=1;i<=cnt;++i)
    		{
    			if(ksm1(x,(p-1)/d[i],p)==1) return 0;
    		} return 1;
    	}
    	inline void find(int x)
    	{	
    		int t=x-1; cnt=0;
    		for(int i=2;i*i<=t;++i) 
    		{
    			if(t%i==0) 
    			{
    				d[++cnt]=i; 
    				while(t%i==0) t/=i;
    			}
    		} if(t>1) d[++cnt]=t;
    		g=2; while(1) if(check(g,x)) break; else ++g;
    		return ;
    	}
    	int A[N],r[N],B[N];
    	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    	inline int del(int x,int y){return x-y>=0?x-y:x-y+mod;}
    	inline void NTT(int *f,int n,int opt)
    	{
    		for(int i=0;i<n;++i) if(i<r[i]) swap(f[i],f[r[i]]);
    		for(int p=2;p<=n;p<<=1)
    		{
    			int len=p>>1,tmp=ksm(g,(mod-1)/p); 
    			if(opt==-1) tmp=ksm(tmp,mod-2);
    			for(int k=0;k<n;k+=p)
    			{
    				int buf=1;
    				for(int l=k;l<k+len;++l)
    				{
    					int tt=1ll*buf*f[l+len]%mod;
    					f[l+len]=del(f[l],tt);
    					f[l]=add(f[l],tt);
    					buf=1ll*buf*tmp%mod;
    				}
    			}
    		} 
    		if(opt==-1) for(int i=0;i<n;++i) f[i]=1ll*f[i]*ksm(n,mod-2)%mod;
    		return ;
    	}
    	int len,sum,t[N];
    	inline void mul(int *a,int *b,int *c)
    	{	
    		static int A[N],B[N],t[N];
    		for(int i=0;i<len;++i) A[i]=a[i],B[i]=b[i];
    		NTT(A,len,1); NTT(B,len,1);   
    		for(int i=0;i<len;++i) A[i]=1ll*A[i]*B[i]%mod;  	  
    		NTT(A,len,-1);
    		for(int i=0;i<m-1;++i) t[i]=add(A[i],A[i+m-1]);
    		for(int i=0;i<m-1;++i) c[i]=t[i];
    		return ;
    	}
    	signed main()
    	{
    		n=read(); m=read(); x=read(); sz=read();  find(m); 
    		for(int i=0;i<m-1;++i) id[ksm1(g,i,m)]=i; g=3;
    		for(int i=1;i<=sz;++i) s[i]=read(),B[id[s[i]%m]]+=s[i]%m?1:0;
    		len=1; sum=m<<1; while(len<=sum) len<<=1; 
    		for(int i=0;i<len;++i) r[i]=r[i>>1]>>1|((i&1)?len>>1:0);
    		
    		A[id[1]]=1; 
    		for(;n;n>>=1,mul(B,B,B))
    		{
    			if(n&1) mul(A,B,A);
    		} 
    		printf("%d
    ",A[id[x%m]]);
    		return 0;
    	}
    }
    signed main(){return yspm::main();}
    ```**------------恢复内容开始------------**
    # Description
    
    [link](https://www.luogu.com.cn/problem/P3321)
    
    给定一个集合 $s$,和整数 $x,n,m$ ($nle 10^9   mle 8000   x le m-1$)
    
    保证 $forall ain {S},ale m$
    
    每次从集合里面随机选数,一共选 $n$ 次,求选出来的数的乘积 $equiv x  (mod m)$ 的方案数
     
    # Solution
    
    上手一个比较暴力的 $dp$ 
    
    设 $f_{i,j}$ 是长度为 $i$ 的序列,乘积模数为 $j$ 的方案数
    
    转移枚举每个可以被随机出来的数转移就好了
    
    复杂度显然爆炸
    
    $N le 10^9   ?  $ 矩阵乘法?
    
    然而并不会……
    
    ----
    
    有一个比较好的优化:
    
    $$f_{2	imes i,p}=sum_{x	imes yequiv p(mod  m)} f_{i,x}	imes f_{i,y}$$
    
    乘法取模并不能计算,怎么把乘法变加法?
    
    套 $log$ 用原根,即:
    
    $$f_{2	imes i,log_{g} p}=sum_{log_{g}x \%m+log _ {g} y\%m=log_{g} p\% m } f_{i,log_{g}x}	imes f_{i,log_{g}y}$$
    
    直接 $NTT$ 卷上 $log (n)$ 次即可
    
    # Code
    
    ```cpp
    #include<bits/stdc++.h>
    using namespace std;
    //#define int long long
    namespace yspm{
    	inline int read()
    	{
    		int res=0,f=1; char k;
    		while(!isdigit(k=getchar())) if(k=='-') f=-1;
    		while(isdigit(k)) res=res*10+k-'0',k=getchar();
    		return res*f;
    	}
    	const int mod=1004535809,N=50010,L=8010;
    	int g,d[100],cnt,n,m,x,sz,s[L],id[L];
    	inline int ksm1(int x,int y,int mod)
    	{
    		int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod;
    		return res;
    	}
    	inline int ksm(int x,int y)
    	{
    		int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod;
    		return res;
    	}
    	inline bool check(int x,int p)
    	{
    		for(int i=1;i<=cnt;++i)
    		{
    			if(ksm1(x,(p-1)/d[i],p)==1) return 0;
    		} return 1;
    	}
    	inline void find(int x)
    	{	
    		int t=x-1; cnt=0;
    		for(int i=2;i*i<=t;++i) 
    		{
    			if(t%i==0) 
    			{
    				d[++cnt]=i; 
    				while(t%i==0) t/=i;
    			}
    		} if(t>1) d[++cnt]=t;
    		g=2; while(1) if(check(g,x)) break; else ++g;
    		return ;
    	}
    	int A[N],r[N],B[N];
    	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    	inline int del(int x,int y){return x-y>=0?x-y:x-y+mod;}
    	inline void NTT(int *f,int n,int opt)
    	{
    		for(int i=0;i<n;++i) if(i<r[i]) swap(f[i],f[r[i]]);
    		for(int p=2;p<=n;p<<=1)
    		{
    			int len=p>>1,tmp=ksm(g,(mod-1)/p); 
    			if(opt==-1) tmp=ksm(tmp,mod-2);
    			for(int k=0;k<n;k+=p)
    			{
    				int buf=1;
    				for(int l=k;l<k+len;++l)
    				{
    					int tt=1ll*buf*f[l+len]%mod;
    					f[l+len]=del(f[l],tt);
    					f[l]=add(f[l],tt);
    					buf=1ll*buf*tmp%mod;
    				}
    			}
    		} 
    		if(opt==-1) for(int i=0;i<n;++i) f[i]=1ll*f[i]*ksm(n,mod-2)%mod;
    		return ;
    	}
    	int len,sum,t[N];
    	inline void mul(int *a,int *b,int *c)
    	{	
    		static int A[N],B[N],t[N];
    		for(int i=0;i<len;++i) A[i]=a[i],B[i]=b[i];
    		NTT(A,len,1); NTT(B,len,1);   
    		for(int i=0;i<len;++i) A[i]=1ll*A[i]*B[i]%mod;  	  
    		NTT(A,len,-1);
    		for(int i=0;i<m-1;++i) t[i]=add(A[i],A[i+m-1]);
    		for(int i=0;i<m-1;++i) c[i]=t[i];
    		return ;
    	}
    	signed main()
    	{
    		n=read(); m=read(); x=read(); sz=read();  find(m); 
    		for(int i=0;i<m-1;++i) id[ksm1(g,i,m)]=i; g=3;
    		for(int i=1;i<=sz;++i) s[i]=read(),B[id[s[i]%m]]+=s[i]%m?1:0;
    		len=1; sum=m<<1; while(len<=sum) len<<=1; 
    		for(int i=0;i<len;++i) r[i]=r[i>>1]>>1|((i&1)?len>>1:0);
    		
    		A[id[1]]=1; 
    		for(;n;n>>=1,mul(B,B,B))
    		{
    			if(n&1) mul(A,B,A);
    		} 
    		printf("%d
    ",A[id[x%m]]);
    		return 0;
    	}
    }
    signed main(){return yspm::main();}
    

    ------------恢复内容结束------------

  • 相关阅读:
    【反射】Java反射机制
    Composer教程之常用命令
    Composer教程之基础用法
    Composer教程之初识Composer
    Composer 的结构详解
    现代 PHP 新特性系列(七) —— 内置的 HTTP 服务器
    现代 PHP 新特性系列(一) —— 命名空间
    现代 PHP 新特性系列(二) —— 善用接口
    现代 PHP 新特性系列(三) —— Trait 概览
    现代 PHP 新特性系列(四) —— 生成器的创建和使用
  • 原文地址:https://www.cnblogs.com/yspm/p/13401693.html
Copyright © 2011-2022 走看看