zoukankan      html  css  js  c++  java
  • [BZOJ2616]SPOJ PERIODNI(笛卡尔树+树形dp)

    题面

    http://darkbzoj.tk/problem/2616

    题解

    前置知识

    先建出笛卡尔树,并定义几个变量:

    (lb[i])表示第i列左侧第一个低于i的位置。

    (rb[i])表示第i列右侧第一个低于i的位置。

    (dh[i]=h[i]-max(h[lb[i]],h[rb[i]]))

    (S_i)描述一个图形:截取原图中的lb[i]+1到rb[i]-1列,并把最下方h[i]行去掉。

    然后就可以描述转移函数:$f[u][x] (表示)S_i$中放入x个车的方案数。

    由于lb[u]+1到rb[u]-1这些点正好构成了笛卡尔树中u的子树,所以转化为一个树形dp。并且,有u的子树大小sz[u]=rb[u]-lb[u]+1。

    for(int i = 0;i <= sz[lc[u]];i++)
    	for(int j = 0;j <= sz[rc[u]];j++)
    		for(int d = 0;i + j + d <= sz[u];d++)	
    			f[u][i+j+d] += f[lc[u]][i] * f[rc[u]][j] % mod * C(sz[u]-i-j,d) % mod * P(dh[u],d) % mod);
    

    其中lc[u],rc[u]代表u的左右子节点,C、P代表组合数与排列数。

    这是为什么呢?(S_u)其实就是(S_{lc[u]})(S_{rc[u]})“中间隔一格”拼在一起,再在下面加上(dh[u])行所得。代码中d枚举的就是下面这dh[u]行中共放了几个车。如果(S_{lc[u]})中放了i个车,(S_{rc[u]})中放了j个车,那么(S_u)中还没被占用的列数就是(sz[u]-i-j)。在这些列中选出无序的d列,再在最下面添加的dh[u]行中选出有序的d行放车,这就解释了上面这个转移方程。

    时间复杂度方面,由于i和j都只枚举到对应的sz,所以总时间复杂度为(O(n^3))

    • P.S.这一过程还可以用卷积优化。

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    #define rg register
    #define In inline
    
    const int N = 500;
    const ll mod = 1e9 + 7;
    const ll W = 1e6;
    
    namespace ModCalc{
    	In void Inc(ll &x,ll y){
    		x += y;if(x >= mod)x -= mod;
    	}
    	In void Dec(ll &x,ll y){
    		x -= y;if(x < 0)x += mod;
    	}
    	In ll Add(ll x,ll y){
    		Inc(x,y);return x;
    	}
    	In ll Sub(ll x,ll y){
    		Dec(x,y);return x;
    	}
    }
    using namespace ModCalc;
    
    int n,k;
    ll jc[W+5],iv[W+5];
    
    ll power(ll a,ll n){
    	ll s = 1,x = a;
    	while(n){
    		if(n & 1)s = s * x % mod;
    		x = x * x % mod;
    		n >>= 1;
    	}
    	return s;
    }
    
    void prepro(){
    	jc[0] = 1;
    	for(rg int i = 1;i <= W;i++)jc[i] = jc[i-1] * i % mod;
    	iv[W] = power(jc[W],mod - 2);
    	for(rg int i = W - 1;i >= 0;i--)iv[i] = iv[i+1] * (i + 1) % mod;
    }
    
    In ll C(ll n,ll m){
    	if(n < m)return 0;
    	return jc[n] * iv[m] % mod * iv[n-m] % mod;
    }
    
    In ll P(ll n,ll m){
    	if(n < m)return 0;
    	return jc[n] * iv[n-m] % mod;
    }
    
    struct CartTree{
    	int top,rt;
    	int fa[N+5],lc[N+5],rc[N+5],sz[N+5];
    	ll h[N+5],dh[N+5],f[N+5][N+5];
    	int st[N+5];	
    	In void build(){
    		for(rg int i = 1;i <= n;i++){
    			scanf("%lld",&h[i]);
    			while(top && h[st[top]] > h[i])
    				lc[i] = st[top--];
    			fa[i] = st[top];
    			if(!fa[i])rt = i;else rc[fa[i]] = i;
    			if(lc[i])fa[lc[i]] = i;
    			st[++top] = i;
    		}
    		f[0][0] = 1;
    	}
    	In void prepro(int u){
    		int l = u;while(lc[l])l = lc[l];
    		int r = u;while(rc[r])r = rc[r];
    		dh[u] = h[u] - max(h[l-1],h[r+1]);
    	}
    	In void dfs(int u){
    		if(lc[u])dfs(lc[u]);
    		if(rc[u])dfs(rc[u]);
    		sz[u] = sz[lc[u]] + sz[rc[u]] + 1;
    		for(rg int i = 0;i <= sz[lc[u]];i++)
    			for(rg int j = 0;j <= sz[rc[u]];j++)
    				for(rg int d = 0;i + j + d <= sz[u];d++)	
    					Inc(f[u][i+j+d],f[lc[u]][i] * f[rc[u]][j] % mod * C(sz[u]-i-j,d) % mod * P(dh[u],d) % mod);
    	}
    }T;
    
    int main(){
    	scanf("%d%d",&n,&k);
    	prepro();
    	T.build();
    	for(rg int i = 1;i <= n;i++)T.prepro(i);
    	T.dfs(T.rt);
    	cout << T.f[T.rt][k] << endl;
    	return 0;
    }
    
  • 相关阅读:
    01-发送你的第一个请求
    postman使用
    java poi导出多sheet页
    base64加密解密
    Django crontab
    super().__init__()
    paramiko模块
    列表转json数据返回
    socket模块判断ip加端口的连通性
    登录拦截器
  • 原文地址:https://www.cnblogs.com/xh092113/p/12383291.html
Copyright © 2011-2022 走看看