Codeforces 285E - Positions in Permutations(二项式反演+dp)
upd on 2021.10.20:修了个 typo(
这是一道 *2600 的 D2E,然鹅为啥我没想到呢?wtcl/dk
首先第一步我就没想到/kk,看到“恰好”二字我们可以想到一个东西叫做二项式反演(qwq 这个套路在刷多项式题时经常见到,可咋换个场景就想不到了呢?显然是我多项式白学了/doge)。我们设 \(f_k\) 表示恰好 \(k\) 个完美数的排列个数,\(g_k\) 表示钦定 \(k\) 个位置满足 \(|p_i-i|=1\),剩下随便乱填的方案数,那么显然对于某个有 \(x\) 个完美位置的排列,它被计入 \(g_y(y<x)\) 的次数为 \(\dbinom{x}{y}\)。
也就是说 \(g_k=\sum\limits_{i=k}^nf_i\dbinom{i}{k}\)。反演一下可得 \(f_k=\sum\limits_{i=k}^n(-1)^{i-k}\dbinom{i}{k}g_i\)。
故我们只需求出 \(g_i\) 就行了。
怎么求 \(g_i\) 呢?这时候就要用到 DP 了,我们设 \(dp_{i,j,x,y}\) 表示填好了前 \(i\) 个位置,钦定了 \(j\) 个位置满足 \(|p_i-i|=1\),\(x\) 表示 \(i\) 是否被选择,\(y\) 表示 \(i+1\) 是否被选择(这一步我又没想到,看来我 DP 也白学了/ww)。转移就分 \(i\) 不是被钦定为“完美位置”,\(p_i=i+1,p_i=i-1\) 三种情况转移即可,具体来说:
- \(dp_{i,j,0,0}=dp_{i-1,j,0,0}+dp_{i-1,j,1,0}+dp_{i-1,j-1,0,0}\)(放 \(i-1\) 或者不被钦定为完美位置)
- \(dp_{i,j,0,1}=dp_{i-1,j-1,0,0}+dp_{i-1,j-1,1,0}\)(\(y=1\),只能放 \(i+1\))
- \(dp_{i,j,1,0}=dp_{i-1,j,0,1}+dp_{i-1,j,1,1}+dp_{i-1,j-1,0,1}\)(放 \(i-1\) 或者不被钦定为完美位置)
- \(dp_{i,j,1,1}=dp_{i-1,j-1,0,1}+dp_{i-1,j-1,1,1}\)(\(y=1\),只能放 \(i+1\))
初始 \(dp_{1,0,0,0}=dp_{1,1,0,1}=1\)。
最后 \(g_k=(dp_{n,k,0,0}+dp_{n,k,1,0})·(n-k)!\)(\(n+1\) 不能被选择),时间复杂度 \(n^2\)。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1000;
const int MOD=1e9+7;
int n,m,dp[MAXN+5][MAXN+5][2][2];
int fac[MAXN+5],ifac[MAXN+5];
void add(int &x,int y){x+=y;if(x>=MOD) x-=MOD;}
int binom(int x,int y){return 1ll*fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;}
int main(){
scanf("%d%d",&n,&m);dp[1][1][0][1]=dp[1][0][0][0]=1;
fac[0]=1;ifac[0]=ifac[1]=1;
for(int i=2;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
for(int i=2;i<=n;i++) for(int j=0;j<=i;j++){
if(j){
add(dp[i][j][0][0],dp[i-1][j-1][0][0]);
add(dp[i][j][1][0],dp[i-1][j-1][0][1]);
add(dp[i][j][0][1],dp[i-1][j-1][0][0]);
add(dp[i][j][0][1],dp[i-1][j-1][1][0]);
add(dp[i][j][1][1],dp[i-1][j-1][0][1]);
add(dp[i][j][1][1],dp[i-1][j-1][1][1]);
}
add(dp[i][j][0][0],dp[i-1][j][0][0]);
add(dp[i][j][0][0],dp[i-1][j][1][0]);
add(dp[i][j][1][0],dp[i-1][j][0][1]);
add(dp[i][j][1][0],dp[i-1][j][1][1]);
} int ans=0;
for(int i=m;i<=n;i++){
int ways=1ll*(dp[n][i][0][0]+dp[n][i][1][0])*fac[n-i]%MOD;
if((i-m)&1) ans=(ans-1ll*ways*binom(i,m)%MOD+MOD)%MOD;
else ans=(ans+1ll*ways*binom(i,m))%MOD;
} printf("%d\n",ans);
return 0;
}
Codeforces 285E - Positions in Permutations(二项式反演+dp)的更多相关文章
- CodeForces - 285E: Positions in Permutations(DP+组合数+容斥)
Permutation p is an ordered set of integers p1, p2, ..., pn, consisting of n distinct positive in ...
- BZOJ3622 已经没有什么好害怕的了 二项式反演+DP
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3622 题解 首先显然如果 \(n - k\) 为奇数那么就是无解.否则的话,"糖果& ...
- CodeForces 340E Iahub and Permutations 错排dp
Iahub and Permutations 题解: 令 cnt1 为可以没有限制位的填充数字个数. 令 cnt2 为有限制位的填充数字个数. 那么:对于cnt1来说, 他的值是cnt1! 然后我们对 ...
- Codeforces 285 E. Positions in Permutations
\(>Codeforces \space 285 E. Positions in Permutations<\) 题目大意 : 定义一个长度为 \(n\) 的排列中第 \(i\) 个元素是 ...
- Codeforces 923E - Perpetual Subtraction(微积分+生成函数+推式子+二项式反演+NTT)
Codeforces 题目传送门 & 洛谷题目传送门 神仙题 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 首先考虑最朴素的 \(dp\),设 \(dp_{z,i}\) 表示经 ...
- P4859 已经没有什么好害怕的了(dp+二项式反演)
P4859 已经没有什么好害怕的了 啥是二项式反演(转) 如果你看不太懂二项式反演(比如我) 那么只需要记住:对于某两个$g(i),f(i)$ ---------------------------- ...
- 洛谷4859 BZOJ3622 已经没什么好害怕的了(DP,二项式反演)
题目链接: 洛谷 BZOJ 题目大意:有两个长为 $n$ 的序列 $a,b$,问有多少种重排 $b$ 的方式,使得满足 $a_i>b_i$ 的 $i$ 的个数比满足 $a_i<b_i$ 的 ...
- BZOJ3622 已经没有什么好害怕的了 【dp + 二项式反演】
题目链接 BZOJ3622 题解 既已开题 那就已经没有什么好害怕的了 由题目中奇怪的条件我们可以特判掉\(n - k\)为奇数时答案为\(0\) 否则我们要求的就是糖果大于药片恰好有\(\frac{ ...
- 51nod 1518 稳定多米诺覆盖(容斥+二项式反演+状压dp)
[传送门[(http://www.51nod.com/Challenge/Problem.html#!#problemId=1518) 解题思路 直接算不好算,考虑容斥,但并不能把行和列一起加进去容斥 ...
随机推荐
- springboot读取配置文件中的信息
在一个项目中,我们有时候会把一些配置信息写入到一个配置文件中,在java代码中读取配置文件的信息.在此记录下读取属性文件中的内容. 在springboot项目中,springboot的配置文件可以使用 ...
- mongodb的索引操作
在mongodb中,当我们一个集合中的数据量非常大时,比如几百万条数据,如果不使用索引,对数据的查询就会进行全表扫描,这个时候查询的速度就会非常的慢,此时我们就需要为集合建立上索引,从而加快查询的速度 ...
- Go并发编程--Mutex/RWMutex
目录 一.前言 二. Mutex 2.1 案例 三. 实现原理 3.1 锁的实现模式 3.2 Go Mutex 实现原理 3.2.1 加锁 3.2.2 解锁 四. 源码分析 4.1 Mutex基本结构 ...
- 替换空格 牛客网 剑指Offer
替换空格 牛客网 剑指Offer 题目描述 请实现一个函数,将一个字符串中的每个空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20A ...
- 一次fork引发的惨案!
"你还有什么要说的吗?没有的话我就要动手了",kill程序最后问道. 这一次,我没有再回答. 只见kill老哥手起刀落,我短暂的一生就这样结束了··· 我是一个网络程序,一直以来都 ...
- git与pycharm的使用详解(git+gitlab+pycham)
前言 当自动化框架搭建出来后,需要多个人来使用框架,写自动化用例. 在这个阶段,我们不可能将写好的代码打包发给其他人,这样很麻烦,多人协作一点也不灵活. 这时候,就提现出了git的价值 一.下载安装 ...
- redhat 7.x 的防火墙软件firewall 介绍
zone 的概念.firewall 一般有9个zone ,配置文件都在 /usr/lib/firewalld/zones/ 里面. 系统的配置文件目录就在 /usr/lib/firewalld 这个目 ...
- 理解前端blob和ArrayBuffer,前端接受文件损坏的问题
1 downloadTemplate().then(res =>{ 2 3 const data = res.data 4 const url = window.URL.createObject ...
- Java 17 新功能介绍(LTS)
点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Jav ...
- OpenXml SDK学习笔记(1):Word的基本结构
能写多少篇我就不确定了,可能就这一篇就太监了,也有可能会写不少. OpenXml SDK 相信很多人都不陌生,这个就是管Office一家的文档格式,Word, Excel, PowerPoint等都用 ...