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

    题面

    Bzoj

    Sol

    pts 1

    大暴力很简单,(f[i][j])表示到第(i)个位置,前面积的模为(j)的方案
    然后可以获得(10)分的好成绩

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int Zsy(1004535809);
    const int _(8010);
    
    IL ll Input(){
        RG ll x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, m, x, S, a[_], f[2][_];
    
    IL void Up(RG int &x, RG int y){
        x += y;
        if(x >= Zsy) x -= Zsy;
    }
    
    int main(RG int argc, RG char* argv[]){
        n = Input(), m = Input(), x = Input(), S = Input();
        for(RG int i = 1; i <= S; ++i) a[i] = Input() % m;
        f[0][1] = 1;
        for(RG int i = 0; i < n; ++i)
            for(RG int j = 0; j < m; ++j){
                RG int lst = i & 1;
                if(!f[lst][j]) continue;
                for(RG int k = 1; k <= S; ++k)
                    Up(f[lst ^ 1][1LL * j * a[k] % m], f[lst][j]);
                f[lst][j] = 0;
            }
        printf("%d
    ", f[n & 1][x]);
        return 0;
    }
    

    pts 2

    你会发现所有的转移都是一样的
    然后你看到(n)的范围就想到了快速幂
    那么把(f)设成一维,(f[i])表示积的模为(i)的方案数
    当前状态下,假设做到了第(k)个位置
    那么此时的(f)数组自己和自己组合就可以转移到第(2k)个位置的(f)
    那么就可以用快速幂一样的方式来优化时间
    (60)

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int Zsy(1004535809);
    const int _(8010);
    
    IL ll Input(){
        RG ll x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, m, x, S, f[_], g[_], h[_];
    
    IL void Up(RG int &x, RG int y){
        x += y;
        if(x >= Zsy) x -= Zsy;
    }
    
    int main(RG int argc, RG char* argv[]){
        n = Input(), m = Input(), x = Input(), S = Input();
        for(RG int i = 1; i <= S; ++i) ++f[Input() % m];
        h[1] = 1;
        for(RG int i = n; i; i >>= 1){
            if(i & 1){
                for(RG int j = 0; j < m; ++j)
                    for(RG int k = 0; k < m; ++k)
                        Up(g[1LL * j * k % m], 1LL * f[j] * h[k] % Zsy);
                for(RG int j = 0; j < m; ++j) h[j] = g[j], g[j] = 0;
            }
            for(RG int j = 0; j < m; ++j)
                for(RG int k = 0; k < m; ++k)
                    Up(g[1LL * j * k % m], 1LL * f[j] * f[k] % Zsy);
            for(RG int j = 0; j < m; ++j) f[j] = g[j], g[j] = 0;
        }
        printf("%d
    ", h[x]);
        return 0;
    }
    

    pts 3

    此时的复杂度为(O(m^2log n))
    那个(log)显然去不掉
    只能优化那个(m^2)
    注意到每次都是(i, j)转移到(i*j)
    如果它是(i, j)转移到(i+j)就好了
    怎么转化?
    你知道可以用指数运算转化
    又注意到(x>=1)(m为质数)
    还是不会

    这个时候就可以去orz 题解辣
    原根!!(大雾)
    原根能干什么?
    注意到原根的性质:
    (g)为质数(p)的原根
    那么(g)(0)(p-2)的幂在模(p)的意义下一定不重不漏对应着(1)(p-1)这些数
    而奇质数((>2))一定有原根
    并且(x>0),那么把积取模后为(0)的丢掉就好了
    那么我们把转移时的(i, j)中的(i, j)映射成原根的幂的指数(i', j')
    那么转移的(i*j)就变成了指数运算(i'+j')(注意这里都是模(m)意义下的)
    然后就可以愉快的(NTT)
    还要注意(NTT)完后得到的数组是比原来长的
    要把它累加到下标对(m-1)取模后的地方
    求原根

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int Zsy(1004535809);
    const int Phi(1004535808);
    const int G(3);
    const int _(20010);
    
    IL ll Input(){
        RG ll x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, m, x, S, f[_], g[_];
    int A[_], B[_], N = 1, l, r[_];
    int mul[_], mp[_];
    
    IL int Pow(RG ll x, RG ll y, RG int zsy){
    	RG ll ret = 1;
    	for(; y; y >>= 1, x = x * x % zsy)
    		if(y & 1) ret = ret * x % zsy;
    	return ret;
    }
    
    IL int Pr_Rt(RG int P){
    	RG int phi = P - 1;
    	for(RG int i = 2; i * i <= phi; ++i)
    		if(phi % i == 0){
    			while(phi % i == 0) phi /= i;
    			mul[++mul[0]] = i;
    		}
    	if(phi > 1) mul[++mul[0]] = phi;
    	for(RG int i = 2; ; ++i){
    		RG int flg = 0;
    		for(RG int j = 1; j <= mul[0]; ++j)
    			if(Pow(i, (P - 1) / mul[j], P) == 1){
    				flg = 1;
    				break;
    			}
    		if(!flg) return i;
    	}
    	return 233;
    }
    
    IL void Prepare(){
    	for(RG int i = m + m - 2; N <= i; N <<= 1) ++l;
    	for(RG int i = 0; i < N; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    	RG int rt = Pr_Rt(m);
    	for(RG int i = 0; i < m - 1; ++i) mp[Pow(rt, i, m)] = i;
    }
    
    IL void NTT(RG int *P, RG int opt){
    	for(RG int i = 0; i < N; ++i) if(i < r[i]) swap(P[i], P[r[i]]);
    	for(RG int i = 1; i < N; i <<= 1){
    		RG int W = Pow(G, Phi / (i << 1), Zsy);
    		if(opt == -1) W = Pow(W, Zsy - 2, Zsy);
    		for(RG int j = 0, p = i << 1; j < N; j += p){
    			RG int w = 1;
    			for(RG int k = 0; k < i; ++k, w = 1LL * w * W % Zsy){
    				RG int X = P[k + j], Y = 1LL * w * P[k + j + i] % Zsy;
    				P[k + j] = (X + Y) % Zsy, P[k + j + i] = (X - Y + Zsy) % Zsy;
    			}
    		}
    	}
    	if(opt == -1){
    		RG int inv = Pow(N, Zsy - 2, Zsy);
    		for(RG int i = 0; i < N; ++i) P[i] = 1LL * P[i] * inv % Zsy;
    	}
    }
    
    IL void Mul(RG int *a, RG int *b){
    	for(RG int i = 0; i < N; ++i) A[i] = a[i], B[i] = b[i], a[i] = 0;
    	NTT(A, 1); NTT(B, 1);
    	for(RG int i = 0; i < N; ++i) A[i] = 1LL * A[i] * B[i] % Zsy;
    	NTT(A, -1);
    	for(RG int i = 0; i < N; ++i) (a[i % (m - 1)] += A[i]) %= Zsy;
    }
    
    int main(RG int argc, RG char* argv[]){
    	n = Input(), m = Input(), x = Input(), S = Input();
    	Prepare();
    	for(RG int i = 1; i <= S; ++i){
    		RG int a = Input() % m;
    		if(a) ++f[mp[a]];
    	}
    	g[mp[1]] = 1;
    	for(; n; n >>= 1, Mul(f, f)) if(n & 1) Mul(g, f);
    	printf("%d
    ", g[mp[x]]);
        return 0;
    }
    
  • 相关阅读:
    笔记12:maven项目管理工具
    笔记11:Oracle基础
    笔记10:springMVC
    笔记9:spring
    笔记8:mybatis
    Bad Sequence
    Optimal Currency Exchange
    Two Small Strings
    Equalizing by Division (easy version)&&(hard version)
    Codeforce 1096:D. Easy Problem(DP,思维)
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8443356.html
Copyright © 2011-2022 走看看