• LuoGuP3321:[SDOI2015]序列统计


    Pre

    错误百出。

    第一次打多项式快速幂。

    Solution

    可以发现用多项式优化动态规划的转移。

    每加入一个数,就乘上一个多项式(其实这个多项式有一点像生成函数,指数表示的数模意义下的值,系数表示的是方案的数量)。

    这样就可以用多项式快速幂优化了。

    于是我就(WA)了一发。

    (f(i,j))表示已经有(i)个数,并且指数为(j)的时候的系数值。

    (f(i,j)-sumlimits_{m*n=j}f(i-1,m)*f(i-1,n))

    于是貌似不可做。

    我尝试着从修改(NTT)的运算过程的角度思考,但是失败了。

    看题解发现

    敲黑板重点

    (g)(m)的原根

    (f(i,log_gj)=sumlimits_{loh_gm+log_gn=log_gj}f(i-1,log_gm)*f(i-1,log_gn))

    可做。

    Code

    #include <bits/stdc++.h>
    #define xx first
    #define yy second
    #define ll long long
    using namespace std;
    const int N = 17000 + 5, mod = 1004535809;
    int nn, nm, x, s, n, bit, rg;
    int f[N], g[N], h[N], val[N], rev[N];
    inline int solve (int);
    inline int mul (int u, int v) {return 1LL * u * v % mod;}
    inline int add (int u, int v) {return u + v >= mod ? u + v - mod : u + v;}
    inline int mns (int u, int v) {return u - v < 0 ? u - v + mod : u - v;}
    inline int qpow (int u, int v, int md) {int tot = 1, base = u % md;while (v) {if (v & 1) {tot = 1LL * tot * base % md;}base = 1LL * base * base % md;v >>= 1;}return tot;}
    inline void solve2 ();
    inline void ntt (int *a, bool flag) {
    	for (int i = 0; i < n; ++i) {
    		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    		if (i > rev[i]) swap (a[i], a[rev[i]]);
    	}
    	for (int l = 2; l <= n; l <<= 1) {
    		int wi = qpow (flag ? qpow (3, mod - 2, mod) : 3, (mod - 1) / l, mod);
    		int m = l / 2;
    		for (int *k = a; k != a + n; k += l) {
    			int w = 1;
    			for (int i = 0; i < m; ++i) {
    				int tmp = mul (k[i + m], w);
    				k[i + m] = mns (k[i], tmp);
    				k[i] = add (k[i], tmp);
    				w = mul (w, wi);
    			}
    		}
    	}
    	int inver = qpow (n, mod - 2, mod);
    	for (int i = 0; i < n && flag; ++i) {
    		a[i] = mul (a[i], inver);
    	}
    }
    inline void work (int *a, int *b) {
    	ntt (a, false);
    	ntt (b, false);
    	for (int i = 0; i < n; ++i) a[i] = mul (a[i], b[i]);
    	ntt (a, true);
    	ntt (b, true);
    	for (int i = nm - 1; i < (nm - 1) * 2; ++i) a[i - (nm - 1)] = add (a[i - (nm - 1)], a[i]), a[i] = 0;
    }
    inline void fpow (int k) {
    	while (k) {
    		if (k & 1) work (f, g);
    		for (int i = 0; i < n; ++i) h[i] = g[i];
    		work (g, h);
    		k >>= 1;
    	}
    }
    int main () {
    	scanf ("%d%d%d%d", &nn, &nm, &x, &s);
    	n = 1, bit = 0;
    	while (n <= nm - 1) n <<= 1, ++bit;
    	n <<= 1, ++bit;
    	rg = solve (nm);
    	solve2 ();
    	for (int i = 1; i <= s; ++i) {int tmp; scanf ("%d", &tmp); if (tmp % nm == 0) continue; g[val[tmp % nm]]++;}
    	f[val[1]] = 1;
    	fpow (nn);
    	printf ("%d
    ", f[val[x]]);
    	return 0;
    }
    inline void solve2 () {
    	int tg = 1;
    	for (int i = 0; i < nm - 1; ++i) {
    		val[tg] = i;
    		tg = 1LL * tg * rg % nm;
    	}
    }
    int q[N], top;
    inline int solve (int p) {
    	top = 0;
    	int x = p - 1;
    	for (int i = 2; i * i <= x; ++i) {
    		if (x % i == 0) {
    			q[++top] = i;
    			while (x % i == 0) {
    				x /= i;
    			}
    		}
    	}
    	if (x > 1) q[++top] = x;
    	for (int i = 2; ; ++i) {
    		bool flag = true;
    		for (int j = 1; j <= top; ++j) {
    			if (qpow (i, (p - 1) / q[j], nm) == 1) flag = false;
    		}
    		if (flag) {
    			return i;
    		}
    	}
    }
    

    Conclusion

    即使看了题解也不能很快做出来。

    首先注意多项式乘法,就是(work())函数,一定要把(b)数组还原(可能就我一个人错吧);

    其次注意求原根的时候

    if (x > 1) q[++top] = x;
    

    一定是(>1)

  • 相关阅读:
    图解攻略:轻松在苹果Macbook Air上装Win7
    Redis的安装与idea中的使用
    CAD中如何裁剪需要的区域
    Layui 使用问题汇总
    Instant Run 的操作影响到了代码,导致Android App启动闪退的问题
    Android studio百度地图demo出现230错误,key校验失败
    「小程序JAVA实战」小程序注册界面的开发(29)
    「小程序JAVA实战」小程序和后台api通信(28)
    「小程序JAVA实战」小程序多媒体组件(27)
    「小程序JAVA实战」小程序导航组件(26)
  • 原文地址:https://www.cnblogs.com/ChiTongZ/p/11293877.html
Copyright © 2020-2023  润新知