• SP3734 PERIODNI


    据说是一道笛卡尔树经典例题,本蒟蒻拿来加强一下理解的(只简单地总结下为什么用笛卡尔树,以及笛卡尔树的性质,之后树形DP的细节就不讲了),所以想看题解的或许可以转到别的大佬那边去了

    链接:https://www.luogu.com.cn/problem/SP3734

    其实样例已经说的已经很清楚了,两个数会不会相互影响,取决于中间有没有一个高度比它们都小的矩形把它们隔开,如果我们把矩形高度视为数列,而一个区间最小的矩形高度就是瓶颈值,即是关键的,受这个瓶颈值控制,于是对于这种关键取决于最小值之类的题,我们常常用堆结构来限制,如kruskal重构树,又如本题的笛卡尔树.所以我们以数列编号为BST关键字,矩阵高度为堆关键字,即可把一个不规则图形拆成若干个规则的矩形,

    从而解决这道题

    /*SP3734 PERIODNI - Periodni*/
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn = 1e3 + 10;
    int lc[maxn],rc[maxn];
    int dp[maxn][maxn];
    int f[maxn];
    int h[maxn];
    int st[maxn],top;
    int siz[maxn];
    bool nrt[maxn];
    const int mod = 1e9 + 7;
    const int maxsize = 1e6 + 10;
    int fa[maxn];
    int jc[maxsize],invjc[maxsize];
    int n,cnt;
    int C(int x,int y){
    	return 1ll * jc[x] * invjc[y] % mod * invjc[x-y] % mod;
    }
    int qpow(int x,int y){
    	int ans = 1;
    	while(y){
    		if(y & 1)	ans = 1ll * ans * x % mod;
    		x = 1ll * x * x % mod;
    		y >>= 1;
    	}
    	return ans;
    }
    int read(){
    	char c = getchar();
    	int x = 0;
    	while(c < '0' || c > '9')		c = getchar();
    	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
    	return x;
    }
    int Add(int x,int y){
    	x += y;
    	return (x >= mod)?x - mod:x;
    }
    void treedp(int x){
    	siz[x] = 1; 	
    	int H = h[x] - h[fa[x]];
    	dp[x][0] = 1;
    //	cout<<x<<endl;
    	if(lc[x]){
    		fa[lc[x]] = x,treedp(lc[x]);
    		memcpy(f,dp[x],sizeof(f));
    		memset(dp[x],0,sizeof(dp[x]));
    		for(int i = 0; i <= siz[x]; ++i){
    			for(int j = 0; j <= siz[lc[x]]; ++j)
    				dp[x][i + j] = Add(dp[x][i + j],1ll * f[i] * dp[lc[x]][j] % mod);
    		}
    		siz[x] += siz[lc[x]];
    	}
    	if(rc[x]){
    		fa[rc[x]] = x,treedp(rc[x]);
    		memcpy(f,dp[x],sizeof(f));
    		memset(dp[x],0,sizeof(dp[x]));
    		for(int i = 0; i <= siz[x]; ++i){
    			for(int j = 0; j <= siz[rc[x]]; ++j)
    				dp[x][i + j] = Add(dp[x][i + j],1ll * f[i] * dp[rc[x]][j] % mod);
    		}
    		siz[x] += siz[rc[x]];
    	}
    	memcpy(f,dp[x],sizeof(f));
    	memset(dp[x],0,sizeof(dp[x]));
    	for(int i = 0; i <= siz[x]; ++i){
    		if(i > H)	break;
    		for(int j = 0; j <= siz[x] - i; ++j){
    			dp[x][i+j] = Add(dp[x][i+j],1ll * f[j] * C(siz[x] - j,i) % mod * jc[i] % mod * C(H,i) % mod);
    		}
    	}
    }
    int main(){
    	jc[0] = invjc[0] = 1;
    	int mx = 1e6;
    	for(int i = 1; i <= mx; ++i)		jc[i] = 1ll * jc[i-1] * i % mod;
    	invjc[mx] = qpow(jc[mx],mod-2);
    	for(int i = mx - 1; i >= 0; --i)	invjc[i] = 1ll * invjc[i+1] * (i + 1) % mod;
    	n = read(),cnt = read();
    	for(int i = 1; i <= n; ++i)		h[i] = read();
    	for(int i = 1; i <= n; ++i){
    		int k = top;
    		while(h[st[k]] > h[i])		k--;
    		if(k)	rc[st[k]] = i;
    		if(k != top)	lc[i] = st[k+1];
    		st[++k] = i;
    		top = k;
    	}
    	for(int i = 1; i <= n; ++i)		nrt[lc[i]] = nrt[rc[i]] = true;
    	int rt = 0;
    	for(int i = 1; i <= n; ++i){
    		if(!nrt[i]){
    			rt = i;
    			break;
    		}
    	}
    	treedp(rt);
    //	cout<<dp[3][0]<<endl;
    	printf("%d
    ",dp[rt][cnt]);
    	return 0;
    }
    

      

  • 相关阅读:
    代码点与代码单元
    IIS最大并发连接数
    PhoneGap:JS跨域请求
    字符串长度
    android学习笔记:adb更换端口后成功启动
    java学习笔记:eclipse的workspace和working set
    java学习笔记:Eclipse打开现有项目
    java学习笔记:文件名区分大小写
    mysql学习笔记:存储过程
    mySql学习笔记:比sql server书写要简单
  • 原文地址:https://www.cnblogs.com/y-dove/p/13669573.html
Copyright © 2020-2023  润新知