zoukankan      html  css  js  c++  java
  • 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("
    ")
    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;
    }
    
  • 相关阅读:
    .net基础学java系列(一)视野
    技术栈
    Apollo(阿波罗)携程开源配置管理中心
    .NET 动态调用WCF
    RPC 工作原理
    ServiceStack 简单使用
    PRC 框架选择
    栈vs堆,最详细的对比
    使用SuperSocket打造逾10万长连接的Socket服务
    开源项目练习EF+jQueryUI前后端分离设计
  • 原文地址:https://www.cnblogs.com/coldchair/p/12787932.html
Copyright © 2011-2022 走看看