其实说实在 我在写这篇博客的时候 才刚刚草了一道这样类型的题 之前几乎没有接触过 接触过也是平时比赛的 没有系统的做过 可以说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=;
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=;i<=N;i++) scanf("%d%d",&pr[i].first,&pr[i].second);
sort(pr+,pr+N+,Cmp); int ans1=,ans2=; memset(F,,sizeof(F)); for(int i=;i<=N;)
{
int now=i; while(now<N&&pr[now].first==pr[now+].first) now++;
for(int j=i;j<=now;j++)
{
ans1=(ans1*(min(pr[j].second,i)+j-i))%Mod; if(j==i){F[j][]=; for(int k=;k<min(pr[j].second,i)+j-i;k++) F[j][k]=(F[j][k-]+F[j][k])%Mod;}
else for(int k=;k<min(pr[j].second,i)+j-i;k++) F[j][k]=(F[j][k-]+F[j-][k-])%Mod;
}
int sum=; for(int j=;j<min(pr[now].second,i)+now-i;j++) sum+=F[now][j];
ans2=(ans2*sum)%Mod; i=now+;
}
return printf("%d %d",ans1,ans2),;
}
/*
2
1 2
2 2
*/

Codeforce 382.E Ksenia and Combinatorics

好难啊啊啊 好想死啊 做了我一天半

题目有一个最大匹配数 有一个不同子树形态 数学老师其实教过我们做题要有化归思想

单单求子树形态我们可以有两种方法:

1.强制左树小于右树且左树和右树相同时 最小的数在左树

2.强制左树小于右树且相同时 /2

单单求最大匹配数我们可以有  F[i][0/1]表示与不与根匹配 转移显然

然后要合起来 怎么办呢 定义F[N][K][0/1]表示N个点最大匹配数为k然后最上面的点选不选

然后我们固定一个根

复制一下别人的图 其实C(k,i-1)就是选一边的 根节点固定不能变 然后因为上一个状态继承下一个状态的时候 下一个状态的根节点是可以变得 所以要两边分别乘k和(i-k-1)

然后没了 好多细节有点难打 注意相同节点个数的时候要除2(画图大概意会了一下想了好久不会证,大概是所有形态只是选的方式不同 形态是一样的) k=0是要*1

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef long long LL;
const LL Mod=1e9+;
LL N,K; LL C[][]; LL F[][][];
void Pre()
{
C[][]=; for(LL i=;i<=N;i++) C[i][]=;
for(LL i=;i<=N;i++) for(LL j=;j<=i;j++) C[i][j]=(C[i-][j-]+C[i-][j]);
}
int main()
{
scanf("%I64d%I64d",&N,&K); Pre();
F[][][]=; F[][][]=;
for(LL p=;p<=N;p++)
{
for(LL q=;q<=p/;q++)
{
for(LL i=;i<=(p-)/;i++)
{
for(LL j=;j<=i/;j++)
{
LL k0=,k1=; if(i==p-i-) k0++; if(i==p-i-) k1++;
LL A=i; LL B=p-i-; if(A==) A++; LL cz=((C[p-][i]/k0)*A*B)%Mod;
cz=(cz*F[i][j][])%Mod; cz=(cz*F[p-i-][q-j][])%Mod;
F[p][q][]=(F[p][q][]+cz)%Mod; cz=((C[p-][i]/k1)*A*B)%Mod;
cz=(cz*((F[i][j][]*F[p-i-][q-j-][])%Mod+(F[i][j][]*F[p-i-][q-j-][])%Mod+(F[i][j][]*F[p-i-][q-j-][])%Mod))%Mod;
F[p][q][]=(F[p][q][]+cz)%Mod;
}
}
}
}
printf("%I64d\n",(F[N][K][]+F[N][K][])%Mod);
return ;
}
hdu5713 K个联通块

绝世好题啊(不是bzoj4300..)

首先N这么小 就应该想到什么组合容斥状压 然后又有一个K 很明显这是要转移的

F[sta][K]表示点的状态为sta 分成K份的方案数 那么我们考虑转移 先在状态中抽出一个点 然后和其余的点互相成为联通块

比如说这个状态有4个点 1 2 3 4 我把1抽出来 使得1 12 13 14 123 124 134成为联通块设为上一个状态s1 然后求解 F[sta][K]=Sigma(F[s1][1],F[sta^s1][k-1])

这样就没有遗漏的 这是liao教我的 因为这样保证了你1这个联通块是不同的 然后其他的块随便取 这样就保证结果都是不同的 而且1是一定要取得,全部都会取完

也就是1总得要属于一个联通块 然后你把联通块枚举出来 其它一定不同 而且一定要算了

但是我们需要做的是F[sta][1] 其实的话这个状态就可以转换成G[sta]-H[sta] G是这个状态所有的边的方案数 通过2^(边数)可以求出 然后H就是这个状态不联通的个数

H的求法类似于F H[sta]=H[s1]*G[sta^s1] 就是随便抽一个点出来 然后其它怎么连都不关我事 这样就每个和这个点的联通块都过了一变 保证不重复

求子集有点妙 自己看看代码意会

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define Maxn 15
using namespace std;
typedef long long LL;
const int Mod=1e9+;
struct node{int x,y,next;}edge[Maxn*Maxn*]; int len,first[Maxn];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
int T,K,N,M; inline int low_bit(int x){return x&(-x);} LL G[<<Maxn]; LL F[Maxn][<<Maxn]; LL H[<<Maxn];
LL bit[Maxn*Maxn];
int main()
{
scanf("%d",&T);
for(int Tcase=;Tcase<=T;Tcase++)
{
scanf("%d%d%d",&N,&M,&K); len=; memset(first,-,sizeof(first));
for(int i=;i<=M;i++){int a,b; scanf("%d%d",&a,&b); if(a>=b) ins(a,b); else ins(b,a);}
bit[]=; for(int i=;i<=M;i++) bit[i]=(bit[i-]<<)%Mod;
memset(F,,sizeof(F)); memset(G,,sizeof(G)); memset(H,,sizeof(H)); for(int i=;i<(<<N);i++)
{
int sum=;
for(int x=;x<=N;x++)
for(int k=first[x];k!=-;k=edge[k].next)
{
int y=edge[k].y;
if((i&(<<(x-)))&&(i&(<<(y-))))
sum++;
}
G[i]=bit[sum];
} for(int i=;i<(<<N);i++)
{
int last=i-low_bit(i);
for(int j=last;j;j=(j-)&last)
H[i]=(H[i]+(F[][i-j]*G[j]))%Mod;
F[][i]=(G[i]-H[i]+Mod)%Mod;
} for(int k=;k<=K;k++)
for(int i=;i<(<<N);i++)
{
int last=i-low_bit(i);
for(int j=last;j;j=(j-)&last)
F[k][i]=(F[k][i]+(F[][i-j]*F[k-][j]))%Mod;
} printf("Case #%d:\n",Tcase);
int ans=; printf("%lld\n",F[K][(<<N)-]);
}
return ;
}

[DP之计数DP]的更多相关文章

  1. POJ 1037 (计数 + DP) 一个美妙的栅栏

    这道题总算勉勉强强看懂了,DP和计数都很不好想 DP部分: 称i根木棒的合法方案集合为S(i),第二根木棒比第一根长的方案称作UP方案,反之叫做DOWN方案 C[i][k][DOWN] 是S(i)中以 ...

  2. HDU5800 To My Girlfriend 背包计数dp

    分析:首先定义状态dp[i][j][s1][s2]代表前i个物品中,选若干个物品,总价值为j 其中s1个物品时必选,s2物品必不选的方案数 那么转移的时候可以考虑,第i个物品是可选可可不选的 dp[i ...

  3. CodeForces 176B Word Cut (计数DP)

    Word Cut Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit St ...

  4. BZOJ 1833: [ZJOI2010]count 数字计数( dp )

    dp(i, j, k)表示共i位, 最高位是j, 数字k出现次数. 预处理出来. 差分答案, 对于0~x的答案, 从低位到高位进行讨论 -------------------------------- ...

  5. HDU4815/计数DP

    题目链接[http://acm.hdu.edu.cn/showproblem.php?pid=4815] 简单说一下题意: 有n道题,每到题答对得分为a[ i ],假如A不输给B的最小概率是P,那么A ...

  6. HDU 6377 度度熊看球赛 (计数DP)

    度度熊看球赛 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  7. 计数dp

    计数dp 计数类的$dp$没做过几个,所以之前都放到"思维"标签下了,后来发现原来这属于一类问题啊...搬过来了. 管道取珠:https://www.lydsy.com/Judge ...

  8. [SDOI2010]地精部落[计数dp]

    题意 求有多少长度为 \(n\) 的排列满足 \(a_1< a_2> a_3 < a_4 \cdots\) 或者 $a_1> a_2 < a_3 > a_4\cdo ...

  9. 【BZOJ】2111: [ZJOI2010]Perm 排列计数 计数DP+排列组合+lucas

    [题目]BZOJ 2111 [题意]求有多少1~n的排列,满足\(A_i>A_{\frac{i}{2}}\),输出对p取模的结果.\(n \leq 10^6,p \leq 10^9\),p是素数 ...

随机推荐

  1. Asp.Net MVC 进阶篇:路由匹配 实现博客路径 和文章路径

    Asp.Net MVC 进阶篇:路由匹配 实现博客路径 和文章路径 我们要实现 通过路由 匹配出 博客地址 和博客文章地址 例如下面的这两个地址 //http://www.cnblogs.com/ma ...

  2. 依赖注入(IOC)二

    依赖注入(IOC)二 上一章我们讲了构造注入与设值注入,这一篇我们主要讲接口注入与特性注入. 接口注入 接口注入是将抽象类型的入口以方法定义在一个接口中,如果客户类型需要获得这个方法,就需要以实现这个 ...

  3. 一个简单的EXTJS案例

    aria-form.js Ext.require([ 'Ext.form.*', 'Ext.layout.container.Column', 'Ext.tab.*' ]); Ext.onReady( ...

  4. C#制作高仿360安全卫士窗体2

    C#制作高仿360安全卫士窗体 继上次C#制作高仿360安全卫士窗体<一>发布之后响应还不错,我的博客放肆雷特也来了不少的新朋友,在这里先谢谢大家的支持!我自己也反复看了一下觉得对不起大家 ...

  5. 分布式EventBus的Socket实现 - 发布订阅

    分布式EventBus的Socket实现 - 发布订阅 在这篇文章中,EventBus实现 - 发布订阅 - XML加载 所适用的范围只是本机的事件传播,要是牵涉到多台服务器之间的事件传播就不行了,解 ...

  6. Arduino 各种模块篇 RGB LED灯

    示例代码: 类似与这样的led,共阴rgb led,通过调节不同的亮度,组合成不同的颜色. 示例代码: /* 作者:极客工坊 时间:2012年12月18日 IDE版本号:1.0.1 发布地址:www. ...

  7. All about Performing User-Managed Database Recovery

    Automatic Recovery with SET AUTORECOVERY ======================================== Issuing SET AUTORE ...

  8. 设计模式08---设计模式之抽象工厂模式(Abstract Factory)

    1.场景模拟 举个生活中常见的例子:组装电脑,我们在组装电脑的时候,通常要选择一系列的配件,比如选择CPU时候,需要注意品牌,型号,针脚数目,主频,只有这些都确定下来,才能确定一个CPU.同样,主板也 ...

  9. 框架基础:ajax设计方案(三)---集成ajax上传技术

    之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询.这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的l ...

  10. 根据字节码探讨java自增运算符的原理

    public class Test { static int x, y; public static void main(String args[]) { x++; myMethod(); Syste ...