其实说实在 我在写这篇博客的时候 才刚刚草了一道这样类型的题 之前几乎没有接触过 接触过也是平时比赛的 没有系统的做过 可以说0基础
我所理解的计数dp就是想办法去达到它要的目的 而且一定要非常劲非常快 都是一个很小的数然后有很多种接下来的方案使得这个数一下子变很大
计数DP常用的有:组合和排列 然后要抽象的想 还有容斥定理(这的话经常考而且很难几乎不会做) 还有用前缀之类的进行优化转移 找到规律就可以搞了
慢慢给出例题慢慢说慢慢学 因为这个要不全AC要不全WA
| [JLOI2013]地形生成 |
计数dp的第一题 看了题意一下子懵比 感受到了计数dp的厉害性 表示连暴力都不会打
然后看题解 自己推了做了死吭了一天 (妈的旁边合唱室的脑瓜有病啊我去 叫了一整天还不给我唱征服..)
首先我们发现 如果小的插在大的前面是完全没有影响的 所以我们按两个关键字从大到小排 这样的话以后插入都没有影响
看上去好像第一问简单一点 就是求合法的标号序列 也就是合法的序列有多少个 那么想一想嘛 对于同一个高度的 你找到前面的你能插到哪里 再加上高度相同的而且在前面的
为什么要加上前面的呢 你想想 前面是不是也是找前面的 那么他们找了的话其实我当前的可以把这个位置忽略掉 并且前面的还会把它找的前面给挤出去 然后还多了个位置
比如说 x y 都在我承受的范围内 就有三个可以放的地方但是加上了前面高度相同的还有一个p 也就是变成了 x p y 那么是不是多了个位置啊 就有四个地方可以插入了
然后就是高度序列的问题 高度序列其实想一想就是同一个高度的 然后两两的位置不能变 这也和我们两个关键字排序有关 小的在前大的在后 这样的话保证高度相同时后面的可以放在前面去
不能交换的话 那么怎么做呢 其实也很简单 想想就好
设F[i][j]表示现在是第i座山放在第j个位置上 占时对于这个高度有多少种方法
那么这个高度第一座山所有这座山能放位置的F[i][j]=1
考虑下一座山 不能放在上一座山的前面 不然就相当于这一座山是第一座 上一座是第二座 就交换了
所以的话F[i+1][j]=sigma(F[i][1..j-1]) 就是上一座山放在我前面的都可以继承下来 然后的话搞一个前缀和优化一下
F[i+1][j]=F[i+1][j-1]+F[i][j-1] 也就是说前面能选的我也可以选 前面不可以选的我可以选就是前面那座山在前面的位置
然后每个最后的山的状态的和加起来就好了 这就代表了这个高度的山一起放的方案数 然后乘一下就好
代码贼短不用怕
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #define Maxn 1010 using namespace std; const int Mod=2011; pair<int,int>pr[Maxn]; bool Cmp(const pair<int,int> &x,const pair<int,int> &y){if(x.first!=y.first) return x.first>y.first; else return x.second<y.second;} int F[Maxn][Maxn]; int main() { int N; scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d%d",&pr[i].first,&pr[i].second); sort(pr+1,pr+N+1,Cmp); int ans1=1,ans2=1; memset(F,0,sizeof(F)); for(int i=1;i<=N;) { int now=i; while(now<N&&pr[now].first==pr[now+1].first) now++; for(int j=i;j<=now;j++) { ans1=(ans1*(min(pr[j].second,i)+j-i))%Mod; if(j==i){F[j][0]=1; for(int k=1;k<min(pr[j].second,i)+j-i;k++) F[j][k]=(F[j][k-1]+F[j][k])%Mod;} else for(int k=0;k<min(pr[j].second,i)+j-i;k++) F[j][k]=(F[j][k-1]+F[j-1][k-1])%Mod; } int sum=0; for(int j=0;j<min(pr[now].second,i)+now-i;j++) sum+=F[now][j]; ans2=(ans2*sum)%Mod; i=now+1; } return printf("%d %d",ans1,ans2),0; } /* 2 1 2 2 2 */