zoukankan      html  css  js  c++  java
  • 洛谷 P5131 荷取融合

    DP

    一个值得思考的 (DP) 题。

    (f[i][j]) 为前 (i) 个中选了 (j) 次的方案数, (g[i][j]) 为前 (i) 个中选了 (j) 次的所有方案的价值积的和。枚举上一个选取的位置和次数即可.

    (O(n^2k^2)) (20)分 空间也会爆炸

    #include<iostream>
    #include<cstdio>
    #define LL long long
    using namespace std;
    int n, k;
    LL tot, sum;
    const int N = 100010, mod = 19260817;
    int a[N];
    LL f[N][305], g[N][305];
    LL ksm(LL a, LL b, LL mod)
    {
    	LL res = 1;
    	for (; b; b >>= 1, a = a * a % mod)
    		if (b & 1)res = res * a % mod;
    	return res;
    }
    int main()
    {
    	cin >> n >> k;
    	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
    	f[0][0] = 1; g[0][0] = 1;
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= k; ++j)
    			for (int l = 0; l < i; ++l)
    				for (int o = 0; o < j; ++o)(f[i][j] += f[l][o]) %= mod;
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= k; ++j)
    			for (int l = 0; l < i; ++l)
    				for (int o = 0; o < j; ++o)(g[i][j] += g[l][o] * ksm(a[i], j - o, mod)) %= mod;
    	for (int i = 1; i <= n; ++i)(tot += f[i][k]) %= mod, (sum += g[i][k]) %= mod;
    	cout << sum*ksm(tot, mod - 2, mod) % mod;
    	return 0;
    }
    

    发现枚举上一个位置并没有什么用,记录一下前缀和就行了。

    另外空间也只需要一维就行。

    (O(nk^2)) (70)

    #include<iostream>
    #include<cstdio>
    #define LL long long
    using namespace std;
    int n, k, now;
    LL tot, sum;
    const int N = 100010, mod = 19260817;
    int a[N];
    LL f[305], g[305], F[305], G[305];
    LL ksm(LL a, LL b, LL mod)
    {
    	LL res = 1;
    	for (; b; b >>= 1, a = a * a % mod)
    		if (b & 1)res = res * a % mod;
    	return res;
    }
    int main()
    {
    	cin >> n >> k;
    	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
    	F[0] = G[0] = 1; now = 1;
    	for (int i = 1; i <= n; ++i)
    	{
    		for (int j = k; j >= 1; --j)
    		{
    			f[j] = g[j] = 0;
    			for (int o = 0; o < j; ++o)(f[j] += F[o]) %= mod;
    			(F[j] += f[j]) %= mod;
    			for (int o = 0; o < j; ++o)(g[j] += G[o] * ksm(a[i], j - o, mod)) %= mod;
    			(G[j] += g[j]) %= mod;
    		}
    		(tot += f[k]) %= mod; (sum += g[k]) %= mod;
    	}
    	cout << sum*ksm(tot, mod - 2, mod) % mod;
    	return 0;
    }
    

    发现循环(o)的含义就是类似于前缀和,差分一下就行。((g) 的差分需要将快速幂拆开)

    (O(nk)) (80)

    #include<iostream>
    #include<cstdio>
    #define LL long long
    using namespace std;
    int n, k, now;
    LL tot, sum;
    const int N = 100010, mod = 19260817;
    int a[N];
    LL f[305], g[305], F[305], G[305], S[305];
    LL ksm(LL a, LL b, LL mod)
    {
    	LL res = 1;
    	for (; b; b >>= 1, a = a * a % mod)
    		if (b & 1)res = res * a % mod;
    	return res;
    }
    int main()
    {
    	cin >> n >> k;
    	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
    	F[0] = G[0] = S[0] = 1; now = 1;
    	for (int i = 1; i <= n; ++i)
    	{
    		for (int j = 1; j <= k; ++j)f[j] = (f[j - 1] + F[j - 1]) % mod;
    		for (int j = 1; j <= k; ++j)(F[j] += f[j]) %= mod;
    		for (int j = 1; j <= k; ++j)S[j] = (S[j - 1] + G[j] * ksm(ksm(a[i], mod - 2, mod), j, mod)) % mod;
    		for (int j = 1; j <= k; ++j)g[j] = ksm(a[i], j, mod) * S[j - 1] % mod;
    		for (int j = 1; j <= k; ++j)(G[j] += g[j]) %= mod;
    		(tot += f[k]) %= mod; (sum += g[k]) %= mod;
    	}
    	cout << sum*ksm(tot, mod - 2, mod) % mod;
    	return 0;
    }
    

    上面的方法虽然时间复杂度对了,但由于快速幂跑的慢,递推省掉快速幂就行了。

    (O(nk)) (100)

    #include<iostream>
    #include<cstdio>
    #define LL long long
    using namespace std;
    int n, k;
    LL tot, sum;
    const int N = 100010, mod = 19260817;
    int a[N];
    int f[305], g[305], F[305], G[305], S[305];
    inline int ksm(int a, LL b)
    {
    	int res = 1;
    	for (; b; b >>= 1, a = (LL)a * a % mod)
    		if (b & 1)res = (LL)res * a % mod;
    	return res;
    }
    inline int work(int x) {return x >= mod ? x - mod : x;}
    int main()
    {
    	cin >> n >> k;
    	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
    	F[0] = G[0] = S[0] = 1;
    	for (int i = 1; i <= n; ++i)
    	{
    		for (int j = 1; j <= k; ++j)f[j] = work(f[j - 1] + F[j - 1]);
    		for (int j = 1; j <= k; ++j)F[j] = work(F[j] + f[j]);
    		for (int j = 1, inv = ksm(a[i], mod - 2), now = 1; j <= k; ++j)now = (LL)now * inv % mod, S[j] = work(S[j - 1] + (LL)G[j] * now % mod);
    		for (int j = 1, now = 1; j <= k; ++j)now = (LL)now * a[i] % mod, g[j] = (LL)now * S[j - 1] % mod;
    		for (int j = 1; j <= k; ++j)G[j] = work(G[j] + g[j]);
    		tot = work(tot + f[k]); sum = work(sum + g[k]);
    	}
    	cout << sum*ksm(tot, mod - 2) % mod;
    	return 0;
    }
    
  • 相关阅读:
    Android文件操作说明

    d
    关于<验证码>的实现和执行流程 http://www.cnblogs.com/androidhtml5/archive/2012/05/19/2533650.html
    div 嵌套 图片
    在线FLV播放器实现方法
    转载 android webview js
    d
    d
    消息
  • 原文地址:https://www.cnblogs.com/wljss/p/12534475.html
Copyright © 2011-2022 走看看