解题:SPOJ 3734 Periodni
按列高建立笛卡尔树,转成树上问题......
笛卡尔树是什么?
它一般是针对序列建立的,是下标的BST和权值的堆(即中序遍历是原序列连续区间,节点权值满足堆性质),这里不讲具体怎么建树(放在知识总结里了)。我们想一想对于一个序列建出来的树长啥样(灵魂画师上线辣)
也就是说树上一个节点对应原图上一个矩形区域,这样我们就把原序列转成了一个组合问题。设$dp[i][j]$表示以$i$为根的子树的区域里放了$j$个车的方案数,那么先是子树里的放法。呃,这不就是树形背包吗。。。转移不写了
然后考虑在自己的矩形里的放法,我们枚举放了$k$个车,那么$dp[i][j]$可以从$dp[i][j-k]$转移过来,过程是这$k$个车排列($k!$)+行上组合($h[i]-h[fa[i]]$里选$k$个)+列上组合($siz[i]-j+k$)
注意:上下两个转移都不要啥也不放就转移,这时候显然是假的=。=
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,M=1e6+,mod=1e9+;
int fac[M],inv[M],fth[N],son[N][];
int a[N],stk[N],siz[N],dp[N][N];
int n,k,top,root;
void Add(int &x,int y)
{
x+=y;
if(x>=mod) x-=mod;
}
int Qpow(int x,int k)
{
if(k==) return x;
int tmp=Qpow(x,k/);
return k%?1ll*tmp*tmp%mod*x%mod:1ll*tmp*tmp%mod;
}
int C(int a,int b)
{
return 1ll*fac[a]*inv[b]%mod*inv[a-b]%mod;
}
void Pre()
{
fac[]=inv[]=;
for(int i=;i<=;i++) fac[i]=1ll*fac[i-]*i%mod;
inv[]=Qpow(fac[],mod-);
for(int i=;i;i--) inv[i]=1ll*inv[i+]*(i+)%mod;
}
void DFS(int nde)
{
siz[nde]=dp[nde][]=;
for(int i=,g;i<=;i++)
if(g=son[nde][i])
{
DFS(g);
for(int j=min(siz[nde],k);~j;j--)
for(int h=min(siz[g],k-j);h;h--)
Add(dp[nde][j+h],1ll*dp[nde][j]*dp[g][h]%mod);
siz[nde]+=siz[g];
}
int col=a[nde]-a[fth[nde]];
for(int i=min(siz[nde],k);~i;i--)
{
int tmp=;
for(int j=min(i,col);j;j--)
Add(tmp,1ll*dp[nde][i-j]*fac[j]%mod*C(col,j)%mod*C(siz[nde]-i+j,j)%mod);
Add(dp[nde][i],tmp);
}
}
int main()
{
scanf("%d%d",&n,&k),Pre();
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
for(int i=;i<=n;i++)
{
while(top&&a[stk[top]]>a[i])
{
int nde=stk[top]; top--;
if(top&&a[stk[top]]>a[i])
son[stk[top]][]=nde,fth[nde]=stk[top];
else
son[i][]=nde,fth[nde]=i;
}
stk[++top]=i;
}
while(top>)
{
son[stk[top-]][]=stk[top];
fth[stk[top]]=stk[top-],top--;
}
// for(int i=1;i<=n;i++) printf("%d %d\n",son[i][0],son[i][1]);
root=stk[],DFS(root);
printf("%d",dp[root][k]);
return ;
}
解题:SPOJ 3734 Periodni的更多相关文章
- [BZOJ2616]SPOJ PERIODNI 树形dp+组合数+逆元
2616: SPOJ PERIODNI Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 128 Solved: 48[Submit][Status][ ...
- 【BZOJ2616】SPOJ PERIODNI 笛卡尔树+树形DP
[BZOJ2616]SPOJ PERIODNI Description Input 第1行包括两个正整数N,K,表示了棋盘的列数和放的车数. 第2行包含N个正整数,表示了棋盘每列的高度. Output ...
- SPOJ QTREE 系列解题报告
题目一 : SPOJ 375 Query On a Tree http://www.spoj.com/problems/QTREE/ 给一个树,求a,b路径上最大边权,或者修改a,b边权为t. #in ...
- BZOJ.2616.SPOJ PERIODNI(笛卡尔树 树形DP)
BZOJ SPOJ 直观的想法是构建笛卡尔树(每次取最小值位置划分到两边),在树上DP,这样两个儿子的子树是互不影响的. 令\(f[i][j]\)表示第\(i\)个节点,放了\(j\)个车的方案数. ...
- Spring-2-H Array Diversity(SPOJ AMR11H)解题报告及测试数据
Array Diversity Time Limit:404MS Memory Limit:0KB 64bit IO Format:%lld & %llu Descript ...
- Spring-2-J Goblin Wars(SPOJ AMR11J)解题报告及测试数据
Goblin Wars Time Limit:432MS Memory Limit:0KB 64bit IO Format:%lld & %llu Description Th ...
- Spring-2-B Save the Students(SPOJ AMR11B)解题报告及测试数据
Save the Students Time Limit:134MS Memory Limit:0KB 64bit IO Format:%lld & %llu Descri ...
- BZOJ2616 SPOJ PERIODNI(笛卡尔树+树形dp)
考虑建一棵小根堆笛卡尔树,即每次在当前区间中找到最小值,以最小值为界分割区间,由当前最小值所在位置向两边区间最小值所在位置连边,递归建树.那么该笛卡尔树中的一棵子树对应序列的一个连续区间,且根的权值是 ...
- BZOJ2616 : SPOJ PERIODNI
长为$A$,宽为$B$的矩阵放$K$个车的方案数$=C(A,K)\times C(B,K)\times K!$. 建立笛卡尔树,那么左右儿子独立,设$f[i][j]$表示$i$子树内放$j$个车的方案 ...
随机推荐
- Python3入门(一)——概述与环境安装
一.概述 1.python是什么 Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 是一种解释型语言: 这意味着开发过程中没有了编译这个环节.类似于PHP和 ...
- c# SSH ,SFTP
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- 【Win32 API】利用SendMessage实现winform与wpf之间的消息传递
原文:[Win32 API]利用SendMessage实现winform与wpf之间的消息传递 引言 有一次心血来潮,突然想研究一下进程间的通信,能够实现消息传递的方法有几种,其中win32ap ...
- Luogu P1983 车站分级
(一周没写过随笔了) 这道题有坑! 看到题目,发现这么明显(??)的要求顺序,还有什么想法,拓扑! 将每条路范围内等级大于等于它的点(不能重复(坑点1))和它连一条边,注意起点终点都要有(坑点2),然 ...
- C++中前置声明介绍
前置声明是指对类.函数.模板或者结构体进行声明,仅仅是声明,不包含相关具体的定义.在很多场合我们可以用前置声明来代替#include语句. 类的前置声明只是告诉编译器这是一个类型,但无法告知类型的大小 ...
- JS计算混合字符串长度
用的是正则表达式 var str = ”坦克是tank的音译”; var len = str.match(/[^ -~]/g) == null ? str.length : str.length + ...
- JavaScript实现选项卡(三种方法)
本文实例讲述了js选项卡的实现方法. 一.html代码: <div id="div1"> <input class="active" type ...
- [HNOI2018]转盘[结论+线段树]
题意 题目链接 分析 首先要发现一个结论:最优决策一定存在一种 先在出发点停留之后走一圈 的情况,可以考虑如下证明: 如果要停留的话一定在出发点停留,这样后面的位置更容易取到. 走超过两圈的情况都可以 ...
- aiohttp基本及进阶使用
客户端使用 发起请求 让我们从导入aiohttp模块开始: import aiohttp 好啦,我们来尝试获取一个web页面.比如我们来获取下GitHub的时间轴. async with aiohtt ...
- 高精度加法--C++
高精度加法--C++ 仿照竖式加法,在第一步计算的时候将进位保留,第一步计算完再处理进位.(见代码注释) 和乘法是类似的. #include <iostream> #include < ...