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

    题面

    题解

    (f[i][j]) 代表长度为 (i) 的序列, 乘积模 (m)(j) 的序列有多少个

    转移方程如下

    [f[i + j][C] = sum_{A*Bequiv C pmod{m} }f[i][B] * f[j][A] ]

    复杂度是 (O(nm^2))

    考虑倍增, 用类似快速幂那样的东西

    [f[2 * i][C] = sum_{A*Bequiv C pmod{m} }f[i][B] * f[i][A] ]

    恩, 复杂度变为了 (O(m^2logn))

    继续优化

    上式相当于一个东西, 看到这个地方

    [c[z] = sum_{x*y equiv z pmod m}a[x]b[y] ]

    如果是这样一种形式

    [c[z] = sum_{x+y=z}a[x]b[y] ]

    我们就可以用 NTT 优化了

    我们知道对数可以把乘法转成加法

    但是对数是一个实数, 我们需要考虑一个模意义下的对数

    把原根当做底数就可以了, 于是我们将上式转化为

    [c[log_gz] = sum_{log_gx+log_gyequiv log_gzpmod m}a[log_gx]b[log_gy] ]

    考虑到 (log_gx+log_gy) 可能会大于 (m)

    但是它一定不会大于 (2m) , 所以我们对于 (c[z]) 这个位置, 加上 (c[z + m - 1]) , 再将 (c[z + m - 1]) 清零即可

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <map>
    const int N = 40005;
    const int mod = 1004535809; 
    using namespace std;
    
    int n, m, X, S, lim, cnt, r[N], g, gg, a[N], b[N], res[N], f[N], top, fact[20005]; 
    map<int, int> mp; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    int fpow(int x, int y, int p)
    {
    	int res = 1;
    	for( ; y; y >>= 1, x = 1ll * x * x % p)
    		if(y & 1) res = 1ll * res * x % p;
    	return res; 
    }
    
    int getroot(int x)
    {
    	top = 0; 
    	int rem = x - 1, p = rem; 
    	for(int i = 2; i * i <= x; i++)
    		if(!(rem % i))
    		{
    			fact[++top] = i;
    			while(!(rem % i)) rem /= i; 
    		}
    	if(rem > 1) fact[++top] = rem; 
    	for(int flag = 1, i = 2; i <= p; i++, flag = 1)
    	{
    		for(int j = 1; j <= top && flag; j++)
    			if(fpow(i, p / fact[j], x) == 1) flag = 0;
    		if(flag) return i; 
    	}
    	return -1; 
    }
    
    void ntt(int *p, int opt)
    {
    	for(int i = 0; i < lim; i++) if(i < r[i]) swap(p[i], p[r[i]]);
    	for(int i = 1; i < lim; i <<= 1)
    	{
    		int rt = fpow(opt == 1 ? g : gg, (mod - 1) / (i << 1), mod);
    		for(int j = 0; j < lim; j += (i << 1))
    		{
    			int w = 1;
    			for(int k = j; k < j + i; k++, w = 1ll * w * rt % mod)
    			{
    				int x = p[k], y = 1ll * w * p[k + i] % mod;
    				p[k] = (1ll * x + y) % mod, p[k + i] = (1ll * x - y + mod) % mod; 
    			}
    		}
    	}
    	if(opt == -1)
    	{
    		int inv = fpow(lim, mod - 2, mod);
    		for(int i = 0; i < lim; i++) a[i] = 1ll * a[i] * inv % mod; 
    	}
    }
    
    void mul(int *A, int *B, int *C)
    {
    	for(int i = 0; i < lim; i++) a[i] = A[i], b[i] = B[i];
    	ntt(a, 1), ntt(b, 1);
    	for(int i = 0; i < lim; i++) a[i] = 1ll * a[i] * b[i] % mod;
    	ntt(a, -1); 
    	for(int i = 0; i < m - 1; i++) a[i] = (1ll * a[i] + a[i + m - 1]) % mod, a[i + m - 1] = 0;
    	for(int i = 0; i < lim; i++) C[i] = a[i]; 
    }
    
    int main()
    {
    	n = read <int> (), m = read <int> (), X = read <int> (), S = read <int> (); 
    	g = getroot(m), gg = fpow(g, m - 2, m); 
    	for(int tmp = 1, i = 0; i < m - 1; i++, tmp = 1ll * tmp * g % m) mp[tmp] = i; 
    	for(int x, i = 1; i <= S; i++)
    	{
    		x = read <int> (); 
    		if(x) f[mp[x]]++; 
    	}
    	res[mp[1]] = 1; 
    	for(lim = 1; lim <= 2 * m; lim <<= 1, cnt++); cnt--;
    	for(int i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << cnt);
    	g = getroot(mod), gg = fpow(g, mod - 2, mod); 
    	while(n)
    	{
    		if(n & 1) mul(res, f, res); 
    		mul(f, f, f); 
    		n >>= 1; 
    	}
    	printf("%d
    ", res[mp[X]]); 
    	return 0; 
    }
    
  • 相关阅读:
    CSS多行文字垂直居中的两种方法
    CSS3 选择器——基本选择器
    页面添加锚点的三种方式
    css3动画特效:上下晃动的div
    CSS3图片倒影技术实现及原理
    标准W3C盒子模型和IE盒子模型CSS布局经典盒子模型(转)
    JQuery中操作Css样式的方法
    22.从上往下打印二叉树 Java
    21.栈的压入、弹出序列 Java
    20.包含min函数的栈 Java
  • 原文地址:https://www.cnblogs.com/ztlztl/p/11989296.html
Copyright © 2011-2022 走看看