zoukankan      html  css  js  c++  java
  • 自嗨测试赛2

    任意模数多项式乘法逆

    多项式F与其乘法逆G相乘后,除常数项为1外,其他项系数都为0

    由此列出式子,移项后就很明显能O(n^2)求出G1~Gn了

    后面其实就是个矩阵加速数列递推了,特判完前n项后,直接矩阵快速幂就完了

    Code
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int maxk = 15;
    int n, K, mod, f[maxk], g[maxk];
    
    struct Mat {
    	int a[maxk][maxk];
    	Mat() { memset(a, 0, sizeof a); }
    	void Init() {
    		for(int i = 1;i <= K; ++i) a[i][i] = 1;
    	}
    	Mat operator * (const Mat &B) const {
    		Mat ans;
    		for(int i = 1;i <= K; ++i) {
    			for(int j = 1;j <= K; ++j) {
    				for(int k = 1;k <= K; ++k) {
    					(ans.a[i][j] += (ll)a[i][k]*B.a[k][j]%mod)%=mod;
    				}
    			}
    		}
    		return ans;
    	}
    };
    
    Mat fp(Mat a, int x) {
    	Mat ans; ans.Init();
    	for(;x > 0;x >>= 1, a = a*a)
    		if(x&1) ans = ans*a;
    	return ans;
    }
    
    int main() {
    	freopen("poly.in","r",stdin);
    	freopen("poly.out","w",stdout);
    	scanf("%d%d%d", &n, &K, &mod);
    	for(int i = 1;i <= K; ++i) scanf("%d", &f[i]);
    	g[0] = 1;
    	for(int i = 1;i <= K; ++i) {
    		int now = 0;
    		for(int j = 1;j <= i; ++j) (now -= (ll)f[j]*g[i-j]%mod)%=mod;
    		g[i] = (now%mod+mod)%mod;
    	}
    	if(n <= K) return printf("%d
    ", g[n]), 0;
    	Mat F, G;
    	for(int i = 1;i <= K; ++i) G.a[i][1] = g[K-i+1];
    	for(int i = 1;i <= K; ++i) F.a[1][i] = mod-f[i];
    	for(int i = 2;i <= K; ++i) F.a[i][i-1] = 1;
    	G = fp(F, n-K)*G;
    	printf("%d
    ", G.a[1][1]);
    	return 0;
    }
    

    明明的随机数

    • 先求出k1行k2列各行各列都不相同的情况,要用到n^2递推第一类斯特林数,斯特林反演

    • 然后乘以第二类斯特林数S2(n,k1)*S2(m,k2)就行了,这两个数可以O(klogn)求

    对于第一步直接搬题解了

    Code
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int mod = 998244353;
    int n, m, c, k1, k2;
    int s1[3005][3005], fac[3005], fy[3005], f[3005], g[3005];
    
    int fp(int a, int x, int ans = 1) {
    	for(;x > 0;x >>= 1, a = (ll)a*a%mod) if(x&1) ans = (ll)ans*a%mod;
    	return ans;
    }
    
    int C(int x, int y) {
    	return (x<y||y<0) ? 0 : (ll)fac[x]*fy[y]%mod*fy[x-y]%mod;
    }
    
    void Init() {
    	s1[0][0] = fac[0] = fy[0] = 1;
    	for(int i = 1;i <= 3000; ++i) for(int j = 1;j <= i; ++j) s1[i][j] = (s1[i-1][j-1]+(ll)s1[i-1][j]*(i-1)%mod)%mod;
    	for(int i = 1;i <= 3000; ++i) fac[i] = (ll)fac[i-1]*i%mod;
    	fy[3000] = fp(fac[3000], mod-2);
    	for(int i = 3000;i > 1; --i) fy[i-1] = (ll)fy[i]*i%mod;
    }
    
    int S2(int n, int m) {
    	int S = 0;
    	for(int i = 0;i <= m; ++i) {
    		if(i&1) (S += mod-(ll)C(m, i)*fp(m-i, n)%mod)%=mod;
    		else (S += (ll)C(m, i)*fp(m-i, n)%mod)%=mod;
    	}
    	return (ll)S*fy[m]%mod;
    }
    
    int main() {
    	freopen("random.in","r",stdin);
    	freopen("random.out","w",stdout);
    	Init();
    	int cs; scanf("%d", &cs);
    	while(cs --> 0){
    		scanf("%d%d%d%d%d", &n, &m, &k1, &k2, &c);
    		if(c == 1) { printf("%d
    ", k1==1&&k2==1);continue; }
    		if(k1 == k2 && k1 == 1) { printf("%d
    ", c%mod);continue; }
    		for(int i = 1;i <= k1; ++i) {
    			int now = 1, lst = fp(c, i);
    			for(int j = 1;j <= k2; ++j) now = (ll)now*(lst-j+1)%mod;
    			f[i] = (now%mod+mod)%mod;
    			g[i] = 0;
    			for(int j = 1;j <= i; ++j) {
    				if((i-j)&1) (g[i] += mod-(ll)s1[i][j]*f[j]%mod)%=mod;
    				else (g[i] += (ll)s1[i][j]*f[j]%mod)%=mod;
    			}
    		}
    		printf("%lld
    ", (ll)g[k1]*S2(n, k1)%mod*S2(m, k2)%mod);
    	}
    	return 0;
    }
    

    凯爹博弈

    又现学了点博弈论,SG函数,不过这道题先咕了

    Code
    
    
  • 相关阅读:
    WQS二分
    虚树
    洛谷集训队题单Part1
    动态点分治
    点分治
    最小乘积模型
    线段树分治
    分层图最短路
    学长学姐们的测试-2
    线性dp
  • 原文地址:https://www.cnblogs.com/Lour688/p/14518320.html
Copyright © 2011-2022 走看看