TopCoder SRM 489 Div1 Lev3:AppleTree
挺优秀的一道题,想出做法时有些惊艳。
题意:
数轴上有\(D\)个连续整数刻度,有\(N\)棵树要种在这些刻度上,其中第\(i\)棵与两旁(如果有的话)相邻的树至少要相距\(R_i\),问方法数。
\(1 \leq N , R_i \leq 40\)
思路:
首先,如果确定了种树的顺序,就确定了相邻树的最小间距。把\(D\)减掉最小间距之和,所得的就是“冗余刻度”的数量。
把这个数量分配给\(N+1\)段间隙,用插板法可以求出方法数。
所以问题在于,对于每一个\(L\),求出1到\(N\)的排列\(P\)的数量,满足:
\]
注意到,对于使\(R_i\)最大的\(i\),它的两侧种的是什么树,不影响这两段间隙的最小长度。
根据套路,这个时候我们应该在\(i\)的位置把排列割开并分别处理。
对于一个1到\(N\)的子集的长度为\(l\)的排列\(P\),定义其代价为:\(\sum_{i=1}^{l-1} \mathrm{max}(R_{P_i}, R_{P_{i+1}})\)。
我们先把\(R\)数组从小到大排序,接着DP:\(dp[i][j][k]\)表示,1到\(i\)这\(i\)个数,组成了\(j\)个不相交排列,排列的代价总和为\(k\)的方法数。
转移时考虑第\(i\)个数在一个排列的两端还是中间,删除之,加上第\(i\)个数两侧的空隙长度并转移即可。把树排序的意义在于,排序后第\(i\)棵树一定是\([1,i]\)中\(R\)值最大的那个。
注意到\(i,j \leq 40\),\(k \leq 1600\),故复杂度没有问题。
等等,正确性有问题……
因为长度为1的排列只有一个端点,而在先前的状态中很难区分长度为1和大于1的排列,故无法求出转移时乘的系数。
所以不妨直接考虑从长度为1的那些排列入手,发现……可以用它们合并成更长的排列?
于是想到可以DP状态不变,转移做如下修改:
首先,初始状态\(dp[0][0][0]=1\)。
其次,允许通过以下方式转移:
1、把\(i+1\)单独组成一个长为1的排列
2、任选两个排列,把\(i\)放在中间将其连接
3、任选一个排列,把\(i\)吸附在其端点旁边
这样把\(i+1\)添加进当前的排列集合之后,就可以转移到\(i'=i+1\)的情况。
对于每一个\(L\),求出1到\(N\)的排列\(P\)的数量,满足\(\sum_{i=1}^{N-1} \mathrm{max}(R_{P_i}, R_{P_{i+1}})=L\)
而对于每一个\(L\),要求的值就是\(dp[N][1][L]\)。Bingo!
代码:
// BEGIN CUT HERE
// END CUT HERE
#line 5 "AppleTrees.cpp"
#include <bits/stdc++.h>
using namespace std;
#define iinf 2000000000
#define linf 1000000000000000000LL
#define ulinf 10000000000000000000ull
#define MOD 1000000007LL
#define lson(v) ((v)<<1)
#define rson(v) (((v)<<1)^1)
#define mpr make_pair
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned long UL;
typedef unsigned short US;
typedef pair < int , int > pii;
clock_t __stt;
inline void TStart(){__stt=clock();}
inline void TReport(){printf("\nTaken Time : %.3lf sec\n",(double)(clock()-__stt)/CLOCKS_PER_SEC);}
template < typename T > T MIN(T a,T b){return a<b?a:b;}
template < typename T > T MAX(T a,T b){return a>b?a:b;}
template < typename T > T ABS(T a){return a>0?a:(-a);}
template < typename T > void UMIN(T &a,T b){if(b<a) a=b;}
template < typename T > void UMAX(T &a,T b){if(b>a) a=b;}
int n,dp[42][42][1655],fac[100005];
int inv(int val){
int ret=1,tms=MOD-2;
while(tms){
if(tms&1) ret=((LL)ret*(LL)val)%MOD;
val=((LL)val*(LL)val)%MOD;
tms>>=1;
}
return ret;
}
int C(int n,int m){
if(n<m) return 0;
if(!n) return 1;
int u=fac[n],d=((LL)fac[m]*(LL)fac[n-m])%MOD;
return ((LL)u*(LL)inv(d))%MOD;
}
class AppleTrees {
public:
int theCount(int D, vector <int> r){
sort(r.begin(),r.end());
n=(int)r.size();
int i,j,k,res=0;
fac[0]=1;
for(i=1;i<=D;++i){
fac[i]=((LL)fac[i-1]*(LL)i)%MOD;
}
dp[0][0][0]=1;
for(i=0;i<n;++i){
for(j=0;j<=n;++j){
for(k=0;k<=1620 && k<=D;++k){
if(!dp[i][j][k]) continue;
dp[i+1][j+1][k]+=dp[i][j][k];
dp[i+1][j+1][k]%=MOD;
dp[i+1][j][k+r[i]]+=((LL)dp[i][j][k]*2LL*(LL)j)%MOD;
dp[i+1][j][k+r[i]]%=MOD;
if(j>1){
dp[i+1][j-1][k+2*r[i]]+=((LL)dp[i][j][k]*(LL)j*(LL)(j-1))%MOD;
dp[i+1][j-1][k+2*r[i]]%=MOD;
}
}
}
}
for(i=1;i<=1620 && i<=D;++i){
res+=((LL)dp[n][1][i-1]*(LL)C(D-i+n,n))%MOD;
res%=MOD;
}
return res;
}
};
TopCoder SRM 489 Div1 Lev3:AppleTree的更多相关文章
- Topcoder Srm 726 Div1 Hard
Topcoder Srm 726 Div1 Hard 解题思路: 问题可以看做一个二分图,左边一个点向右边一段区间连边,匹配了左边一个点就能获得对应的权值,最大化所得到的权值的和. 然后可以证明一个结 ...
- Topcoder SRM 643 Div1 250<peter_pan>
Topcoder SRM 643 Div1 250 Problem 给一个整数N,再给一个vector<long long>v; N可以表示成若干个素数的乘积,N=p0*p1*p2*... ...
- topcoder srm 714 div1
problem1 link 倒着想.每次添加一个右括号再添加一个左括号,直到还原.那么每次的右括号的选择范围为当前左括号后面的右括号减去后面已经使用的右括号. problem2 link 令$h(x) ...
- Topcoder SRM 602 div1题解
打卡- Easy(250pts): 题目大意:rating2200及以上和2200以下的颜色是不一样的(我就是属于那个颜色比较菜的),有个人初始rating为X,然后每一场比赛他的rating如果增加 ...
- Topcoder SRM 627 div1 HappyLettersDiv1 : 字符串
Problem Statement The Happy Letter game is played as follows: At the beginning, several players ...
- Topcoder SRM 584 DIV1 600
思路太繁琐了 ,实在不想解释了 代码: #include<iostream> #include<cstdio> #include<string> #include& ...
- TopCoder SRM 605 DIV1
604的题解还没有写出来呢.先上605的. 代码去practice房间找. 说思路. A: 贪心,对于每个类型的正值求和,如果没有正值就取最大值,按着求出的值排序,枚举选多少个类型. B: 很明显是d ...
- topcoder srm 575 div1
problem1 link 如果$k$是先手必胜那么$f(k)=1$否则$f(k)=0$ 通过对前面小的数字的计算可以发现:(1)$f(2k+1)=0$,(2)$f(2^{2k+1})=0$,(3)其 ...
- topcoder srm 635 div1
problem1 link 首先枚举长度$L$.然后计算每一段长度$L$的差值最大公约数,然后差值除以最大公约数的结果可以作为当前段的关键字.然后不同段就可以比较他们的关键字,一样就是可以转化的. p ...
随机推荐
- adb工具包究竟能帮我们做什么?
adb工具包主要作用于什么呢?应该有很多用户都不了解adb,那就一起来了解一下吧!adb的全称为Android Debug Bridge,就是起到调试桥的作用. 借助adb工具,我们可以管理设备或手机 ...
- NO.002-2018.02.07《越人歌》先秦:佚名
参考之后略有修改,疑问点“不訾诟耻”释义 越人歌原文.翻译及赏析_古诗文网 蒙羞被好兮不訾诟耻_释义_吴江诗词网 越人歌 先秦:佚名 今夕何夕兮,搴舟中流.今晚是怎样的晚上啊河中漫游.搴(qiān ...
- git 解决冲突方法
转载:http://www.cnlvzi.com/index.php/Index/article/id/119 当共享一个项目后提交冲突时 git push -f 强制推送本地的替换服务端 git f ...
- note04-计算机网络
4.WLAN无线局域网(wareless local area network) IEEE802.11无线以太网协议标准 基础服务集合BSS 基站AP 服务服务集合标识SSID 即wifi名 分配系统 ...
- 牛客练习赛20(ABC)
A. 礼物 题意: 我从买奥利奥的事情中想出了一个算法题:假设某个店铺有N种不同类型的1元奥利奥和M种不同类型的2元奥利奥,而且余量无限,我的钱有k元,我想把k元都用来买奥利奥,且可以买同类型的奥利奥 ...
- 生理周期,POJ(1006)
题目链接:http://poj.org/problem?id=1006 解题报告: 1.枚举天数的时候可以根据前面的结果直接跳过一些错误的答案. ///三个周期是23,28,33, #include ...
- 深搜(DFS),回溯,Fire Net
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=2 解题报告: 这里的深搜有一点不同,就是,在深搜每一个点时,都要深搜每 ...
- android imageview使用的时候 引用资源src和background的区别
android imageview使用的时候 引用资源时src和background的区别 src更强调内容并且不行拉伸图片进行适配,而background更注重引用图片,会对图片进行拉伸
- socket手写一个简单的web服务端
直接进入正题吧,下面的代码都是我在pycharm中写好,再粘贴上来的 import socket server = socket.socket() server.bind(('127.0.0.1', ...
- 构建vue零散笔记
# vue项目(用webpack构建)的前提是已安装了node.js,vue,vue-cli,webpack # 主要命令构建:vue init webpack 项目名(纯英文,且不可驼峰)运行:np ...