计数 dp 部分例题(十一~十五部分)
十一、矩阵的利用(行列を用いたテクニック)
1. 快速幂(二分累乗)
(1) 推导转移矩阵(行列の導出)
例题:Placing Squares
题解
(2) BM 优化递推(?)(コンパニオン行列の累乗)
(3) 多项式快速幂(多項式の累乗)
将转移矩阵看成乘上一个多项式的形式,则转移的合并可以从 \(O(n^3)\) 优化到 \(O(n\log n)\)。此时如果每个多项式都相同则可以直接使用快速幂,且如果模数为 NTT 模数可以 进一步优化到 \(O(n\log n)\)。
2. 行列式优化计数(行列式のテクニック)
十二、忽略小概率事件(小さい確率を無視する)
在用实数形式表示某个概率或期望时,可以将“极小的贡献”忽略不计,以达到更快速的效果。
例题:Ben Toh
题意
有一个变量 \(x\),初始为 \(0\)。现在需要对其执行 \(n\) 次操作:
- 有 \(2^x\) 的概率将 \(x\) 加上 \(1\)。
- 有 \(1-2^x\) 的概率将 \(x\) 赋为 \(0\)。
求第一次操作被执行的期望次数。
解法
考虑 \(dp\)。设 \(dp_{i,j}\) 为在当前 \(x=j\) 时执行 \(i\) 次操作后 \(x\) 的期望值。转移有 \(dp_{i,j}=2^j(dp_{i-1,j+1}+1)+(1-2^j)dp_{i-1,0}\),初值显然为 \(\forall j,dp_{0,j}=0\),目标为 \(\sum_{j=0}^n dp_{i,j}\)。
考虑在 \(j\) 足够大的时候,\(dp_{i-1,j+1}+1\) 的贡献是可以忽略不计的,对之后的 dp 值的贡献也可以忽略不计,所以只需要维护每个 \(dp_i\) 的前 \(O(\log)\) 项即可。
代码
点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxl=30;
const int maxn=100010;
int n,i,j;
double dp[2][maxl],sm[maxn],pw[maxl];
double w,a,*X=dp[0],*Y=dp[1];
int main(){
for(pw[0]=j=1;j<maxl;++j)
pw[j]=pw[j-1]*0.5;
for(i=1;i<maxn;++i){
for(j=0;j<maxl-1;++j)
Y[j]=pw[j]*(X[j+1]+1)+(1-pw[j])*X[0];
Y[maxl-1]=(1-pw[maxl-1])*X[0];
sm[i]=Y[0]; a=0; swap(X,Y);
for(j=0;j<maxl;++j) Y[j]=0;
}
for(;;){
scanf("%d",&n);
if(!n) return 0;
printf("%.8lf\n",sm[n]);
}
return 0;
}
十三、二项式(二項係数のテクニック)
1. 常用公式(頻出公式集)
- \(\sum_{i=0}^n\binom ni=2^n\)
- \(\sum_{i=0}^{\lfloor\frac n2\rfloor}\binom n{2i}=2^{n-1}\)
- \(\sum_{i=0}^k\binom{n+i}i=\binom{n+i+1}i\)
- \(\sum_{i=0}^k\sum_{j=0}^l\binom{i+j}i=\binom{k+l+2}{k+1}\)
- \(\sum_{i=0}^k\binom ni\binom m{k-i}=\binom{n+m}k\)
- \(\sum_{i=0}^k\binom{n+i}i\binom{m-i}{k-i}=\binom{n+m+1}k\)
2. 转化为路径计数问题/组合数的几何意义(経路数への帰着)
例题1:BBQ Hard
题意
求 \(\sum_{i=1}^n\sum_{j=i+1}^n\binom{a_i+a_j+b_i+b_j}{a_i+a_j}\)。\(n\le 2\times 10^5\)。
解法
考虑 \(\binom{a_i+a_j+b_i+b_j}{a_i+a_j}\) 可以看成 \((-a_i,-b_i)\) 到 \((a_j,b_j)\) 的 只向上/向右走时的路径 数量之和,则原来的和式可以看成 \(\frac 12\left(\sum_{i=1}^n\sum_{j=1}^n\binom{a_i+a_j+b_i+b_j}{a_i+a_j}-\sum_{i=1}^n\binom{2a_i+2b_i}{2a_i}\right)\),前半部分为所有 \((-a_i,-b_i)\) 到所有 \((a_i,b_i)\) 路径数量之和,可以 dp 转移。
代码
点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxx=2010;
const int maxd=maxx<<1;
const int maxv=maxx<<2;
const int maxn=200010;
const int md=1000000007;
int n,i,j,a,b,ans,dp[maxd][maxd];
int x[maxn],y[maxn],fac[maxv],inv[maxv];
inline int Pow(int d,int z){
int r=1;
do{
if(z&1) r=(1LL*r*d)%md;
d=(1LL*d*d)%md;
}while(z>>=1);
return r;
}
inline int C(int x,int y){return ((1LL*fac[y]*inv[x])%md*inv[y-x])%md;}
inline void Add(int &x,int y){x-=((x+=y)>=md)*md;}
int main(){
fac[0]=1;
for(i=1;i<maxv;++i) fac[i]=(1LL*i*fac[i-1])%md;
inv[maxv-1]=Pow(fac[maxv-1],md-2);
for(i=maxv-1;i;--i) inv[i-1]=(1LL*inv[i]*i)%md;
scanf("%d",&n);
for(i=1;i<=n;++i){
scanf("%d%d",&a,&b);
++dp[maxx-a][maxx-b];
Add(ans,C(a<<1,(a+b)<<1));
x[i]=maxx+a; y[i]=maxx+b;
}
ans=md-ans;
for(i=2;i<maxd;++i){
for(j=2;j<maxd;++j){
a=dp[i-1][j-1];
Add(dp[i][j-1],a);
Add(dp[i-1][j],a);
}
}
for(i=1;i<=n;++i) Add(ans,dp[x[i]][y[i]]);
printf("%d\n",(1LL*ans*((md+1)>>1))%md);
return 0;
}
3. 旋转 45 度(45 度回転)
例题2:Don't worry. Be Together
题意
有 \(n\) 个点,第 \(i\) 个点的横纵坐标为 \((x_i,y_i)\)。起点为原点,每一步可以向上/下/左/右走一步。设 \(f(i)\) 为走 \(T\) 步刚好能到达 \((x_i,y_i)\) 的方案数,求 \(\prod_{i=1}^n f(i)\bmod mod\)。\(1\le mod\le 10^9+7;n,T\le 10^5;|x_i|,|y_i|\le 10^6\)。
解法
考虑 \(x_i,y_i\ge 0\) 的情况。如果考虑在 \(x/y\) 轴方向正向/反向走了多少步的情况,则和式难以化简。
考虑将整个坐标系逆时针旋转 45 度,将某个点 \((x,y)\) 对应到新坐标系上的 \((x+y,x-y)\),则在原图上向左/上/右/下走一步对应了在新的坐标系上向左下/左上/右上/右下走一步,新的坐标系上每次可以从某个点 \((x,y)\) 走到 \((x\pm 1,y\pm 1)\) 的位置,可以把横纵坐标分别独立出来看,则走 \(T\) 步到新坐标系上的点 \((X,Y)\) (令 \(X,Y\ge 0\))的方案数为 \(\binom{T}{\frac{T-X}2}\binom{T}{\frac{T-Y}2}\)。
注意模数不一定为整数,考虑将最后的答案写成 \(\prod_{i=2}i^{c_i}\) 的形式,则上面的组合数可以拆成某个阶乘除以某个阶乘的形式,乘/除某个阶乘等效于对 \(c\) 前缀加/前缀减,最后处理整个答案时将每个 \(i\) 分解质因数则可以将答案处理出来。
代码
点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000010;
int n,m,i,j,t,x,y,u,w,a=1;
int v[maxn],p[maxn],d[maxn];
long long pw[maxn];
inline int Pow(int d,long long z){
int r=1;
do{
if(z&1) r=(1LL*r*d)%m;
d=(1LL*d*d)%m;
}while(z>>=1);
return r;
}
int main(){
for(i=2;i<maxn;++i){
if(!v[i]) v[i]=p[++t]=i;
for(j=1;j<=t;++j){
u=p[j];
if(v[i]<u||i*u>=maxn) break;
v[i*u]=u;
}
}
scanf("%d%d%d",&n,&t,&m);
while(n--){
scanf("%d%d",&x,&y);
u=x+y; w=x-y;
if(u<0) u=-u;
if(w<0) w=-w;
if(u<w) swap(u,w);
if(t<u||(u^t)&1){
printf("0\n");
return 0;
}
x=(t-u)>>1; y=(t-w)>>1;
d[t]+=2; --d[x]; --d[y];
--d[t-x]; --d[t-y];
}
x=0;
for(i=maxn-1;i;--i){
if(!(x+=d[i])) continue;
for(u=i;u!=1;){
w=v[u]; y=0;
while(v[u]==w) ++y,u/=w;
pw[w]+=1LL*y*x;
}
}
for(i=2;i<maxn;++i){
if(!(u=pw[i])) continue;
a=(1LL*a*Pow(i,u))%m;
}
printf("%d\n",a);
return 0;
}
4. 卡塔兰数(カタラン数)
十四、容斥原理(包除原理)
1. 使用对称性(?)(対称性を用いる場合)
例题1:~K Perm Counting
题意
求 \(1\sim n\) 的排列 \(p\) 中,\(\forall i\) 满足 \(|p_i-i|\ne K\) 的 \(p\) 的个数。\(n\le 2\times 10^3\)。
解法
考虑容斥,计算钦定若干个 \(i\) 满足 \(|p_i-i|=K\) 的对应 \(p\) 的数量。此时可以以下标为左部点,元素为右部点构造二分图,则钦定 \(c\) 个满足 \(|p_i-i|=K\) 的对应方案数即为二分图上大小为 \(c\) 的匹配数量乘以 \((n-c)!\)。考虑该二分图一定可以拆成 \(2\times \min(K,n-K)\) 条链的形式,则对应匹配数量即为在这些链上选取 \(c\) 条边且满足选择的任意两条边不共点的方案数。
代码
点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2010;
const int md=924844033;
int n,i,j,k,u,v,d,dp[2][maxn<<1][2]; bool s[maxn<<1];
inline void Add(int &x,int y){x-=((x+=y)>=md)*md;}
int main(){
scanf("%d%d",&n,&k);
j=n%k; v=n/k;
for(i=u=1;i<=k;++i){
d=v+(i<=j);
s[u]=1; u+=d;
s[u]=1; u+=d;
}
auto X=dp[0],Y=dp[1];
X[0][0]=1; --u;
for(i=1;i<=u;++i){
for(j=0;j<=i;++j) Add(Y[j][0]=X[j][0],X[j][1]);
if(!s[i]) for(j=0;j<i;++j) Add(Y[j+1][1],X[j][0]);
swap(X,Y); memset(Y,0,sizeof(dp[0]));
}
u=0; d=j=1;
for(i=n;~i;--i){
Add(v=X[i][0],X[i][1]);
v=(1LL*v*d)%md; d=(1LL*d*(j++))%md;
if(i&1) v=md-v; Add(u,v);
}
printf("%d\n",u);
return 0;
}
2. 使用 dp(DP を用いる場合)
例题2
题意
有一个平面直角坐标系,初始位置为 \((0,0)\),每次可以向横/纵坐标走一步,同时有 \(n\) 个点不能走,求走到 \((X,Y)\) 的方案数。\(X,Y\le 10^5,n\le 2\times 10^3\)。
解法
首先不可能直接计算如何走得合法的方案数。考虑从 \(n\le 2\times 10^3\) 入手计算不能走得合法得方案数。
显然某个点 \(x\) 能走到另一个不同的点 \(y\) 则一定有 \(y\) 不能走到 \(x\),可以将所有点按照横/纵坐标排序后,每个点只需要考虑之前的点能否到达它。令 \(p(i,j)\) 为从 \(i\) 走到 \(j\) 的方案数,设 \(dp_{i,j}\) 为当前在点 \(i\),经过了 \(j\) 个不能经过的点的方案数,则转移为 \(dp_{i,j}=\sum_{k=1}^{i-1}dp_{k,j-1}p(k,i)\)。显然最后每个 dp 对答案的贡献只和 \(j\) 的奇偶性相关,则可以把奇偶性相同的 \(j\) 的对应 \(dp_{i,j}\) 合并。
3. 对因数的容斥(約数系包除)
例题3
题意
有 \(n\) 个数,求其所有非空子集的 \(\gcd\) 之和。值域,\(n\le 10^5\)。
解法
考虑从大到小判断每个数是多少个非空子集的 \(\gcd\)。显然可以先统计每个数 \(i\) 的倍数个数 \(c_i\),则以其为 公因数 的非空子集为 \(2^{c_i}-1\) 个,但是需要减去 \(i\) 的所有倍数对应子集的个数。
例题4:Rotated Palindromes
题意
对于所有的长为 \(n\),字符集为 \(1\sim k\) 内的整数的回文串 \(a\),在任意次将 \(a\) 的开头的数字移到末尾的操作之后,求能够生成多少种不同的序列。\(n,k\le 10^9\)。
解法
考虑将 \(a\) 的最小循环节移动到末尾之后会形成 \(a\) 本身,所以只需要讨论某个最小循环节带来的贡献即可。显然最小循环节为回文串(和反串相等),则需要考虑该回文串能否经过若干次循环移位之后形成新的回文串。设某个最小循环节 \(S\) 长为 \(d\),如果 \(S\) 能够经过 \(i\) 次操作得到最小循环节 \(T\),则 \(S\) 同样经过 \(d-i\) 次操作也会得到 \(T\)(因为 \(S\) 的长为 \(\min(i,d-i)\) 的前缀和后缀为相等的回文子串)。而上述的 \(i\) 唯一,所以当且仅当 \(i=\frac d2\) 时 \(S\) 能变成 \(T\)。综上,对于长为 \(d\) 的最小循环节,如果 \(d\) 为奇数则贡献为 \(d\),否则贡献为 \(\frac d2\)。
代码
点此查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2700;
const int md=1000000007;
int n,k,i,j,a,u,t,s[maxn],c[maxn];
inline int Pow(int d,int z){
int r=1;
do{
if(z&1) r=(1LL*r*d)%md;
d=(1LL*d*d)%md;
}while(z>>=1);
return r;
}
inline void Add(int &x,int y){x-=((x+=y)>=md)*md;}
int main(){
scanf("%d%d",&n,&k);
u=sqrt(n);
for(i=1;i<=u;++i){
if(!(n%i)){
s[++t]=i;
s[++t]=n/i;
}
}
if(u*u==n) --t;
sort(s+1,s+t+1);
for(i=1;i<=t;++i){
n=s[i]; u=Pow(k,(n+1)>>1);
for(j=1;j<i;++j) if(!(n%s[j])) Add(u,c[j]);
c[i]=md-u; if(!(n&1)) n>>=1; a=(1LL*u*n+a)%md;
}
printf("%d\n",a);
return 0;
}
十五、“难解”的计数问题(「解けない問題」を見極める)
#P 问题是一类对 NP 问题计数的问题,其中有一些计数问题是 #PC 的(可能对应的判定性问题不是 NPC 问题)(这种计数问题暂时不能找到多项式的解法):
所以我们在对计数问题求解时,需要尽量避免直接求解上述问题。
您觉得这几篇文章怎么样呢?今日进行探讨的内容只是 dp 优化方法的一小部分,除此之外我的随笔中还介绍有更多的 dp 优化的方法,我作为一名蒟蒻,衷心地欢迎您来博客园参观。
计数 dp 部分例题(十一~十五部分)的更多相关文章
- 《剑指Offer》题四十一~题五十
四十一.数据流中的中位数 题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中 ...
- [原]Jenkins(十五)---jenkins插件之deploy
/** * lihaibo * 文章内容都是根据自己工作情况实践得出. *如有错误,请指正 * 版权声明:本博客欢迎转发,但请保留原作者信息! http://www.cnblogs.com/horiz ...
- 我的MYSQL学习心得(十五) 日志
我的MYSQL学习心得(十五) 日志 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
- Bootstrap <基础二十五>警告(Alerts)
警告(Alerts)以及 Bootstrap 所提供的用于警告的 class.警告(Alerts)向用户提供了一种定义消息样式的方式.它们为典型的用户操作提供了上下文信息反馈. 您可以为警告框添加一个 ...
- Bootstrap<基础十五> 输入框组
Bootstrap 支持的另一个特性,输入框组.输入框组扩展自 表单控件.使用输入框组,可以很容易地向基于文本的输入框添加作为前缀和后缀的文本或按钮. 通过向输入域添加前缀和后缀的内容,您可以向用户输 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十五):消息加密
前不久,微信的企业号使用了强制的消息加密方式,随后公众号也加入了可选的消息加密选项.目前企业号和公众号的加密方式是一致的(格式会有少许差别). 加密设置 进入公众号后台的“开发者中心”,我们可以看到U ...
- NeHe OpenGL教程 第四十五课:顶点缓存
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- UVA 1252 十五 Twenty Questions
十五 Twenty Questions Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Submi ...
- 别人的的MYSQL学习心得(十五) 日志
我的MYSQL学习心得(十五) 日志 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
- Starling性能优化技巧十五则
Starling的性能优化要点 一.尽可能减少状态变更 如您所知,Starling使用Stage3D来渲染所有的可见对象.这就意味着所有的绘制都是GPU完成的. 现在,Starling可以一个接一个的 ...
随机推荐
- 52道常见Python面试题,你都看过了吗?(转发供参考学习)
https://blog.csdn.net/xiaohei3ge/article/details/88080284?utm_medium=distribute.pc_relevant.none-tas ...
- 复习第6点-6.SpringMVC作用域传值
作用域范围 对象名称 作用范围 application 整个作用范围 session 在当前会话中有效 request 在当前请求中有效 page 在当前页面有效 request/session/ap ...
- Appium获取元素坐标
文章转自:https://www.cnblogs.com/lfr0123/p/13686769.html appium做app自动化测试过程中,有时需要获取控件元素的坐标进行滑动操作.appium中提 ...
- UML 组成 1
常用关系: 关联关系使用一条直线表示,比如 A与B关联 用于描述不同类的对象之间的结构关系,将多个类的实例联系在一起 是一种静态关系,基本与程序的运行没有关系 比如,部门与员工的关系,就是关联关系 ...
- shell 脚本请求接口报错
2023-01-18 22:07:07.984 WARN 11700 --- [io-9044-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : ...
- 至少有K个重复字符的最长子串
传送门 /** * 分治 */ class Solution { // dp[i]:表示以i为结尾满足条件的子串的长度 public int longestSubstring(String s, in ...
- python脚本打包
python脚本打包 Python写脚本很方便,可以直接在机器上运行,但有时候脚本源码不方便透露或是其他机器不支持的原因,需要将其打包成可执行文件,需要用到 pyinstaller 首先下载pip s ...
- Python的入门学习Day 16~18——form”夜曲编程“
Day 16 Day 17 time:2021.8.14. 今天七夕.激动,喜悦.平静呼吸,嘻嘻~ 也许我也是天空.去看课程了,嗯.今天重点学习了循环的对立面--"跳出循环"的 ...
- Zabbix源码安装与yum安装
一.源码安装方式:zabbix-server LAMP环境准备: #groupadd zabbix#useradd -g zabbix zabbix 1.安装依赖包: #yum install gcc ...
- vue3 门户网站搭建6-wangeditor
门户网站的新闻.公告等文章,内容可配置,故引入 wagneditor 1.安装: npm i wangeditor 2.方便调用,抽成组件: <template> <div ref= ...