BZOJ2616 : SPOJ PERIODNI

长为$A$,宽为$B$的矩阵放$K$个车的方案数$=C(A,K)\times C(B,K)\times K!$。

建立笛卡尔树,那么左右儿子独立,设$f[i][j]$表示$i$子树内放$j$个车的方案数。

合并左右儿子之后,枚举在底部矩形放几个车进行转移即可。

时间复杂度$O(n^3)$。

#include<cstdio>
const int N=505,M=1000005,P=1000000007;
int n,m,i,a[N],mx,fac[M],inv[M],g[N],f[N][N];
inline int cal(int a,int b,int k){
  if(a<k||b<k)return 0;
  return 1LL*fac[a]*inv[a-k]%P*inv[k]%P*fac[b]%P*inv[b-k]%P;
}
int dp(int l,int r,int h){
  if(l>r)return 0;
  int i,j,x=l;
  for(i=l;i<=r;i++)if(a[i]<a[x])x=i;
  int u=dp(l,x-1,a[x]),v=dp(x+1,r,a[x]);
  for(i=0;i<=r-l;i++)g[i]=0;
  for(i=0;i<=x-l;i++)if(f[u][i])for(j=0;j<=r-x;j++)g[i+j]=(1LL*f[u][i]*f[v][j]+g[i+j])%P;
  for(i=0;i<=r-l+1;i++)for(f[x][i]=j=0;j<=r-l&&j<=i;j++)f[x][i]=(1LL*g[j]*cal(r-l+1-j,a[x]-h,i-j)+f[x][i])%P;
  return x;
}
int main(){
  scanf("%d%d",&n,&m);
  for(mx=n,i=1;i<=n;i++)scanf("%d",&a[i]),mx=a[i]>mx?a[i]:mx;
  for(fac[0]=i=1;i<=mx;i++)fac[i]=1LL*fac[i-1]*i%P;
  for(inv[0]=inv[1]=1,i=2;i<=mx;i++)inv[i]=1LL*P/i*(P-inv[P%i])%P;
  for(i=2;i<=mx;i++)inv[i]=1LL*inv[i-1]*inv[i]%P;
  return printf("%d",f[dp(f[0][0]=1,n,0)][m]),0;
}

  

时间: 2024-10-10 05:57:16

BZOJ2616 : SPOJ PERIODNI的相关文章

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

不连续的处理很麻烦 导致序列DP又找不到优秀的子问题 自底向上考虑? 建立小根堆笛卡尔树 每个点的意义是:高度是(自己-father)的横着的极大矩形 子问题具有递归的优秀性质 f[i][j]i为根子树,放j个 儿子背包合并 考虑本层的矩形放多少个 枚举一共放t个,本层放j个 对于子树里的放置的t-j个,不论怎么放,一定占据了t-j列,剩下W[i]-(t-j)个位置 转移是: https://blog.csdn.net/qq_39972971/article/details/79359547 当

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

[BZOJ2616]SPOJ PERIODNI Description Input 第1行包括两个正整数N,K,表示了棋盘的列数和放的车数. 第2行包含N个正整数,表示了棋盘每列的高度. Output 包括一个非负整数,表示有多少种放置的方案,输出答案mod 1000000007后的结果即可. Sample Input 5 2 2 3 1 2 4 Sample Output 43 HINT 对于100%的数据,有 N≤500,K≤500,h[i] ≤1000000. 题解:一看题就感觉应该是单调

解题:SPOJ 3734 Periodni

题面 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=550,M=1e6+60,mod=1e9+7; 6 int fac[M],inv[M],fth[N],son[N][2]; 7 int a[N],stk[N],siz[N],dp[N][N]; 8 int n,k,top,root; 9 void Add(int &

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

SPOJ 3273

传送门: 这是一道treap的模板题,不要问我为什么一直在写模板题 依旧只放代码 1 //SPOJ 3273 2 //by Cydiater 3 //2016.8.31 4 #include <iostream> 5 #include <cstring> 6 #include <ctime> 7 #include <cmath> 8 #include <cstdlib> 9 #include <string> 10 #include

SPOJ CRAN02 - Roommate Agreement

题目链接:http://www.spoj.com/problems/CRAN02/ 题目大意:N个数字组成的序列,和为0的连续子序列的个数.N<1e6 解题思路:计算前缀和,统计每个数字出现的次数,那么对于数字sum[i], 如果存在k个sum[i],则代表有C(k, 2)个序列和为0,而如果sum[i] = 0,则还要累加上对应的k值. 代码: 1 ll n; 2 int a[maxn]; 3 ll sum[maxn]; 4 map<int, int> mmp; 5 6 void so

spoj GCJ1C09C Bribe the Prisoners

题目链接: http://www.spoj.com/problems/GCJ1C09C/ 题意: In a kingdom there are prison cells (numbered 1 to P) built to form a straight line segment. Cells number i and i+1 are adjacent, and prisoners in adjacent cells are called "neighbours." A wall wi

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]

BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

2588: Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问.