Codeforces 1158 F. Density of subarrays(找性质+分段dp)

http://codeforces.com/contest/1158/problem/F

先考虑如何求一个序列的density。

假设要求序列\(A[1..n]\)的density。

找到\(A[1..n]\)的最小一个前缀\(A[1..i]\),使其包含\(1-c\)的所有颜色。

那么我们可以 以这些颜色作为那些序列的开头,并且这样一定是最优的,那么\(Density(A[1..n])=Density(A[i+1..n])+1\)

此时我们可以得到一个显然的状压dp:
\(f[i][j][S]\)表示前\(i\)个中,已有\(j\)个包含\(1-c\)的段,最后一个段现在包含\(S\)中的颜色。
时间复杂度:\(O(n^2/c*2^c)\)

事实上可以搞出更快的dp。

设\(g[i][j]\)表示在\(A[i..j]\)中选一个子序列,这个子序列包含\(A[j]\),且恰好包含\(1-c\)的颜色。

“恰好”的定义:选了\(A[j]\)之后才包含\(1-c\)的颜色。

那么枚举\(j\),再倒着枚举\(i\),用一个桶统计每个颜色出现次数,即可算出\(g\)。

同理可以算出\(h[i][j]\)表示在\(A[i..j]\)中选一个子序列,这个子序列包含\(A[j]\),且不能包含\(1-c\)的颜色。

设\(f[i][j]\)表示以\(i\)结尾的子序列中,恰有\(j\)个\(1-c\)的段,利用\(g\)可以很快的转移。

算出\(f\)后,利用\(h\)可以统计答案案。

时间复杂度:\(O(n^3/c)\)

综合两个算法即可通过本题。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 998244353;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int N = 3005;

int n, c, a[N];

ll ans[N];

namespace sub1 {
	ll a2[N], inv[N];
	ll g[N][N], h[N][N], sh[N];
	ll cnt[N], sum, c0;
	void cl() {
		fo(i, 1, c) cnt[i] = 0;
		sum = 1; c0 = c;
	}
	void add(int x) {
		if(!cnt[x]) {
			cnt[x] ++;
			c0 --;
		} else {
			sum = sum * inv[cnt[x]] % mo;
			cnt[x] ++;
			sum = sum * (a2[cnt[x]] - 1) % mo;
		}
	}
	ll f[N][N];
	void work() {
		a2[0] = 1; fo(i, 1, n) a2[i] = a2[i - 1] * 2 % mo;
		fo(i, 0, n) inv[i] = ksm(a2[i] - 1, mo - 2);
		fo(j, 1, n) {
			cl();
			fd(i, j, 1) {
				add(a[i]);
				if(c0) {
					g[i][j] = 0;
					h[i][j] = a2[j - i];
				} else {
					ll v = sum * inv[cnt[a[j]]] % mo;
					g[i][j] = v;
					h[i][j] = (a2[j - i] - v * a2[cnt[a[j]] - 1] % mo + mo) % mo;
				}
			}
		}
		f[0][0] = 1;
		fo(i, 0, n - 1) fo(j, 0, n / c) if(f[i][j]) {
			fo(k, i + 1, n) f[k][j + 1] = (f[k][j + 1] + f[i][j] * g[i + 1][k]) % mo;
		}
		fo(i, 0, n) fo(j, i, n) sh[i] = (sh[i] + h[i][j]) % mo;
		fo(i, 0, n) fo(j, 0, n / c) {
			if(i > 0) ans[j] = (ans[j] + f[i][j]) % mo;
			ans[j] = (ans[j] + f[i][j] * sh[i + 1]) % mo;
		}
	}
}

namespace sub2 {
	int m;
	ll f[2][N][1 << 10]; int o;
	void work() {
		m = (1 << c) - 1;
		f[o][0][0] = 1;
		fo(i, 1, n) {
			fo(j, 0, i / c) fo(s, 0, m) f[!o][j][s] = f[o][j][s];
			fo(j, 0, (i - 1) / c) {
				fo(s, 0, m - 1) if(f[o][j][s]) {
					int ns = s | (1 << (a[i] - 1));
					int nj = j;
					if(ns == m) ns = 0, nj ++;
					f[!o][nj][ns] = (f[!o][nj][ns] + f[o][j][s]) % mo;
				}
			}
			o = !o;
		}
		fo(j, 0, n / c) fo(s, 0, m)
			ans[j] = (ans[j] + f[o][j][s]) % mo;
		ans[0] = (ans[0] - 1 + mo) % mo;
	}
}

int main() {
	scanf("%d %d", &n, &c);
	fo(i, 1, n) scanf("%d", &a[i]);
	if(c > 10)
		sub1 :: work();
	else
		sub2 :: work();
	fo(i, 0, n) pp("%lld ", ans[i]); hh;
}
上一篇:Android屏幕适配(二):AutoSize今日头条屏幕适配方案


下一篇:14.3 balance_pgdat函数