题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2616

把相同高度的连续一段合成一个位置(可能不需要?),用前缀和维护宽度。

然后每次找区间里最低的那个点(ST表)作为根,递归左右孩子,构建笛卡尔树。

dp[ cr ][ j ] 表示在 cr 的子树里选择 j 个点的方案数。

自己本来写的是同时枚举 cr 这个点、ls 、rs 各贡献了多少个车,结果TLE。

看看题解,发现这样比较好(至多 \( n^3 \) ),就是先 \( dp[ cr ][ j ] = \sum dp[ ls ][ k ] * dp[ rs ][ j-k ] ),然后再枚举 cr 的贡献,形如 \( dp[ cr ][ j ] = \sum dp[ cr ][ j-k ] * C_{h}^{k} * C_{w-(j-k)}^{k} * k! \) ,其中 w 表示 cr 这个点的宽,h 表示 cr 这个点的高。

注意那里还要乘一个 \( k! \) 。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int Mn(int a,int b){return a<b?a:b;}
int Mx(int a,int b){return a>b?a:b;} const int N=,K=,M=1e6+,mod=1e9+;
int upt(int x){if(x>=mod)x-=mod;if(x<)x+=mod;return x;}
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;} int n,ht[N],jc[M],jcn[M],dp[N][N];
int bin[K],lg[N],st[N][K],s[N];
struct Dt{
int bh,mx;
Dt(int b=,int m=):bh(b),mx(m) {}
};
void init(int mx)
{
jc[]=;for(int i=;i<=mx;i++)jc[i]=(ll)jc[i-]*i%mod;
jcn[mx]=pw(jc[mx],mod-);
for(int i=mx-;i>=;i--)jcn[i]=(ll)jcn[i+]*(i+)%mod; for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
bin[]=;for(int i=;i<=lg[n];i++)bin[i]=bin[i-]<<;
for(int i=;i<=n;i++)st[i][]=i;
for(int t=;t<=lg[n];t++)
for(int i=;i+bin[t]-<=n;i++)
{
int u=st[i][t-], v=st[i+bin[t-]][t-];
if(ht[u]<ht[v])st[i][t]=u;
else st[i][t]=v;
}
}
int C(int n,int m)
{
if(n<m)return ;//
return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod;
}
int get(int l,int r)
{
int d=lg[r-l+];
int u=st[l][d], v=st[r-bin[d]+][d];
if(ht[u]<ht[v])return u; else return v;
}
Dt solve(int l,int r,int pr)
{
if(l>r)return Dt(,);
int cr=get(l,r), w=s[r]-s[l-], h=ht[cr]-pr;
Dt Ls=solve(l,cr-,ht[cr]); int ls=Ls.bh,m1=Ls.mx;
Dt Rs=solve(cr+,r,ht[cr]); int rs=Rs.bh,m2=Rs.mx;
for(int i=,l1=m1+m2;i<=l1;i++)
for(int j=Mx(,i-m2),l2=Mn(i,m1);j<=l2;j++)
{
dp[cr][i]=(dp[cr][i]+(ll)dp[ls][j]*dp[rs][i-j])%mod;
}
int lm=Mn(h,w), mx=m1+m2+Mn(h,w-m1-m2); dp[cr][]=;
for(int i=mx;i;i--)
for(int j=,l1=Mn(i,lm);j<=l1;j++)
{
dp[cr][i]=(dp[cr][i]+
(ll)dp[cr][i-j]*C(h,j)%mod*C(w-i+j,j)%mod*jc[j])%mod;
}
return Dt(cr,mx);
/*
printf("(%d,%d)cr=%d w=%d h=%d\n",l,r,cr,w,h);
printf(" ls=%d m1=%d rs=%d m2=%d\n",ls,m1,rs,m2);
int mx=m1+m2+Mn(h,w-m1-m2);
printf(" mx=%d\n",mx);
for(int i=1;i<=mx;i++)
{
printf(" i=%d\n",i);
for(int j1=0,l1=Mn(m1,i);j1<=l1;j1++)
for(int j2=Mx(0,i-j1-Mn(h,w-j1)),l2=Mn(i-j1,m2);j2<=l2;j2++)
{
int ret=(ll)dp[ls][j1]*dp[rs][j2]%mod;
int k=i-j1-j2;
ret=(ll)ret*C(h,k)%mod*C(w-j1-j2,k)%mod*jc[k]%mod;//jc[k]
dp[cr][i]=upt(dp[cr][i]+ret);
printf(" j1=%d j2=%d k=%d (dp[%d]=%d)\n"
,j1,j2,k,i,dp[cr][i]);
}
printf(" dp[%d]=%d\n",i,dp[cr][i]);
}
dp[cr][0]=1; return Dt(cr,mx);
*/
}
int main()
{
int tn,tm,mx=;
scanf("%d%d",&tn,&tm);
for(int i=,d,lst=;i<=tn;i++,lst=d)
{
scanf("%d",&d); mx=Mx(mx,d);
if(d!=lst) ht[++n]=d, s[n]=s[n-]+;
else s[n]++;
}
init(mx); dp[][]=;///
Dt Rt=solve(,n,);
printf("%d\n",dp[Rt.bh][tm]);
return ;
}

bzoj 2616 SPOJ PERIODNI——笛卡尔树+树形DP的更多相关文章

  1. BZOJ.2616.SPOJ PERIODNI(笛卡尔树 树形DP)

    BZOJ SPOJ 直观的想法是构建笛卡尔树(每次取最小值位置划分到两边),在树上DP,这样两个儿子的子树是互不影响的. 令\(f[i][j]\)表示第\(i\)个节点,放了\(j\)个车的方案数. ...

  2. 【BZOJ2616】SPOJ PERIODNI 笛卡尔树+树形DP

    [BZOJ2616]SPOJ PERIODNI Description Input 第1行包括两个正整数N,K,表示了棋盘的列数和放的车数. 第2行包含N个正整数,表示了棋盘每列的高度. Output ...

  3. BZOJ2616 SPOJ PERIODNI(笛卡尔树+树形dp)

    考虑建一棵小根堆笛卡尔树,即每次在当前区间中找到最小值,以最小值为界分割区间,由当前最小值所在位置向两边区间最小值所在位置连边,递归建树.那么该笛卡尔树中的一棵子树对应序列的一个连续区间,且根的权值是 ...

  4. bzoj2616: SPOJ PERIODNI——笛卡尔树+DP

    不连续的处理很麻烦 导致序列DP又找不到优秀的子问题 自底向上考虑? 建立小根堆笛卡尔树 每个点的意义是:高度是(自己-father)的横着的极大矩形 子问题具有递归的优秀性质 f[i][j]i为根子 ...

  5. BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)

    题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...

  6. BZOJ 2286 [Sdoi2011]消耗战(虚树+树形DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2286 [题目大意] 出一棵边权树,每次给出一些关键点,求最小边割集, 使得1点与各个关 ...

  7. BZOJ.5287.[AHOI HNOI2018]毒瘤(虚树 树形DP)

    BZOJ LOJ 洛谷 设\(f[i][0/1]\)表示到第\(i\)个点,不选/选这个点的方案数.对于一棵树,有:\[f[x][0]=\prod_{v\in son[x]}(f[v][0]+f[v] ...

  8. bzoj 1040: [ZJOI2008]骑士【基环树+树形dp】

    没考虑可以连着两个不选--直接染色了 实际上是基环森林,对于每棵基环树,dfs找出一个环边,然后断掉这条边,分别对这条边的两端点做一边treedp,取max加进答案里 treedp是设f[u]为选u点 ...

  9. BZOJ2616 SPOJ PERIODNI(笛卡尔树 + DP)

    题意 N,K≤500,h[i]≤106N,K\le 500,h[i]\le10^6N,K≤500,h[i]≤106 题解 建立出小根堆性质的笛卡尔树,于是每个节点可以代表一个矩形,其宽度为子树大小,高 ...

随机推荐

  1. 进程退出exit、_exit、abort

    分为正常退出,异常退出 正常退出的方法: 1.在main函数中执行return 2.调用exit函数 3.调用_exit  函数 ----------------------------------- ...

  2. forget word out2

      1★ dictionary / dik ʃ ən əri   dict   2★ fy => faction f æk ʃ ən 派别  

  3. js运动例子1

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...

  4. RTU命令设置笔记

    YN+12VCTL=1 配置+12V输出控制模式:永久输出YN+5VCTL=1 配置+5V输出控制模式:永久输出 YN+GETDATA 读取采样值 YN++LIST 获取设置参数列表 YN+LOAD ...

  5. C# 自动发送邮件被系统当做垃圾邮件退回的处理方法

    前两天做了一个发送邮件的定时任务.其功能为在每天的某一时刻,系统自动从银行查询前一天的交易明细到本地服务器,解析后生成为各自不同Excel文件,并打包成zip压缩文件,然后抄送附件给财务对账.其中,邮 ...

  6. 量身打造自己的MyEclipse(多图)

    迎新年 贺元旦MyEclipse推新版 在线订购低至 7.5 折!截止1月31号(活动期间在线下单的客户才可享受此优惠,过期恢复原价) 立即抢购 1.量身打造你自己的MyEclipse MyEclip ...

  7. Alpha版本展示

    一.团队成员的简介和个人博客地址,团队的源码仓库地址. 1.团队的源码仓库地址 我们主要通过码云来管理代码,码云地址:https://gitee.com/ljl36/team_programming ...

  8. (libev) cannot allocate xxx bytes, aborting 问题的一种触发条件

    最近在项目中使用了libev,遇到一个很奇怪的crash,服务器运行随机时间后有一定的概率奔溃,输出的最后一行log是: (libev) cannot allocate xxx bytes, abor ...

  9. postman-进阶使用

    管理请求 保存请求-添加“打开百度首页请求” 设置请求方式为Get,地址为www.baidu.com.点击右侧保存按钮 在弹出的保存请求中设置保存的内容.Collections:百度,Request ...

  10. Linux环境下 多线程下载 (Python 实现版)

    本文是多年前学习编程时参照一个网友程序的基础之上改写的, 采用Python语音编写, 多线程下载功能, 可以有效提高Linux下原有下载工具中的一些不足,以下给出具体代码. #!/usr/bin/py ...