有关一类容斥计数dp问题

一类计数dp问题

最近才接触到了一些dp最后容斥的题目,还经常和状压,概率期望啥的一起搞事情。由于之前基本没做过类似的题目,就被虐飞了,所以就做了一些题目。。

一般都是比如答案是要求“恰好…”,但是我们难以限制成恰好的情况,所以我们可以试图放宽条件,改成类似于“至少…”的状态。这个时候我们就可以先构造出满足至少的情况,其他的就可以随意组合。然后最后我们要统计答案的话就可以上容斥。注意先想好容斥的复杂度。。

还有一些比较迷的容斥模型,比如子集容斥,min-max容斥啊啥的,可能得要手推公式。随便丢点常用的公式咯:

子集容斥: f [ s ] = ∑ t ⊂ s ( − 1 ) ∣ s ∣ − ∣ t ∣ f [ t ] f[s]=\sum_{t\subset s}(-1)^{|s|-|t|}f[t] f[s]=ts(1)stf[t]

min-max容斥: m a x ( S ) = ∑ t ⊂ s ( − 1 ) ∣ t ∣ − 1 m i n ( t ) max(S)=\sum_{t\subset s}(-1)^{|t|-1}min(t) max(S)=ts(1)t1min(t)

相关题目

  • AGC005D
  • 51nod1518
  • HDU4336
  • BZOJ4036 按位或
  • BZOJ4455
  • BZOJ4767
  • BZOJ4361
  • BZOJ2560
  • BZOJ4005
  • BZOJ4596
  • BZOJ3622
  • BZOJ3294
  • BZOJ2839
  • BZOJ4710
  • CF342D
  • TC SRM498Div1 foxjump 11223

部分题解就直接丢在这下面了…如果有的话

AGC005D ~K Perm Counting

Problem

Atcoder

Solution

位置与值一共是2n个点,可以构造一个这样的二分图,如果我们在相差k的两点间连线。yy画一下这个图,那么这个图就相当于被拆成了几条链。我们把这些链放在一起考虑。

显然答案就是要求没有一条边匹配的方案数。然而这个并不好直接统计。所以我们不妨这样来考虑,设 G [ i ] G[i] G[i]表示至少有i条边匹配上的方案数,那么我们就强制选i条边即可,其他的位置随便乱排。这样就至少有i个不合法的方案。

那我们就对搞下来的这个序列进行dp。记 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示到第i个点选取了j条边且第i-1个点和第i个点之间的边有没有选时的方案数。记 F [ i ] F[i] F[i]表示选取了至少i条边的方案数,那么显然我们有 F [ i ] = ( f [ 2 n ] [ i ] [ 0 ] + f [ 2 n ] [ i ] [ 1 ] ) ∗ ( n − i ) ! F[i]=(f[2n][i][0]+f[2n][i][1])*(n-i)! F[i]=(f[2n][i][0]+f[2n][i][1])(ni)!

我们可以认同这个式子

G [ i ] = F [ i ] − ∑ j = i + 1 n G [ j ] ( j i ) G[i]=F[i]-\sum_{j=i+1}^n G[j]\binom j i G[i]=F[i]j=i+1nG[j](ij)

移项即可得到

F [ i ] = ∑ j = i n G [ j ] ( j i ) F[i]=\sum_{j=i}^n G[j]\binom j i F[i]=j=inG[j](ij)

a n s = G [ 0 ] = ∑ i = 0 n ( − 1 ) i F [ i ] ans=G[0]=\sum_{i=0}^n (-1)^i F[i] ans=G[0]=i=0n(1)iF[i]

为什么这样是对的呢?我们推一推系数就会发现,其他每一项的系数相当于是n=i的组合数被做了一次奇偶性分组,并作差,显然除了 G [ 0 ] = ( 0 0 ) = 1 G[0]=\binom 0 0=1 G[0]=(00)=1之外,其他的系数都为0。

Code

#include 
#define rg register
using namespace std;
typedef long long ll;
const int maxn=2010,mod=924844033;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
	x=0;int f=0;char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=1,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	if(f) x=-x;
}
int n,k,lim,tot,ans,fac[maxn],xu[maxn<<1],link[maxn<<1],f[maxn<<1][maxn][2],g[maxn];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
void get(int id,int op)
{
	while(id<=n)
	{
		xu[++tot]=id;
		if(op) xu[tot]+=n;
		op^=1;id+=k;
	}
	link[tot]=1;
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	read(n);read(k);lim=n<<1;fac[0]=1;
	for(rg int i=1;i<=k;i++) get(i,0),get(i,1);
	for(rg int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%mod;
	f[0][0][0]=1;link[0]=1;
	for(rg int i=1;i<=lim;i++)
	  for(rg int j=0;j<=n;j++)
	  {
	  	if(!link[i-1]&&j) f[i][j][1]=f[i-1][j-1][0];
	  	f[i][j][0]=pls(f[i-1][j][0],f[i-1][j][1]);
	  }
	for(rg int i=0;i<=n;i++)
	{
		g[i]=pls(f[n+n][i][0],f[n+n][i][1]);
		if(i&1) ans=dec(ans,(ll)g[i]*fac[n-i]%mod);
		else ans=pls(ans,(ll)g[i]*fac[n-i]%mod);
	}
	printf("%d\n",ans);
	return 0;
}

BZOJ4361 isn

Problem

BZOJ

Solution

我们用 g [ i ] g[i] g[i]表示长度为i的lis的方案数。求法可以用dp得到,中间用BIT优化一下转移即可。

什么是不合法的方案?那就是最后得出来的lis是由另一个lis删除得到的。
考虑长度为i的lis的多余贡献,首先这i个可以随便删一个,然后其他的删除顺序随意乱排,即 g [ i ] ∗ i ∗ ( n − i ) ! g[i]*i*(n-i)! g[i]i(ni)!

Code

#include 
#include 
#include 
#define rg register
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=2010,mod=1e9+7;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
	x=0;int f=0;char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=1,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	if(f) x=-x;
}
int n,tot,ans,a[maxn],b[maxn],fac[maxn],f[maxn][maxn],g[maxn];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
struct BIT{
	int a[maxn];
	void clear(){memset(a,0,sizeof(a));}
	void add(int p,int v){for(;p<=tot;p+=lowbit(p)) a[p]=pls(a[p],v);}
	int query(int p){int res=0;for(;p;p-=lowbit(p)) res=pls(res,a[p]);return res;}
}t;
void input()
{
	read(n);fac[0]=1;
	for(rg int i=1;i<=n;i++){read(a[i]);b[i]=a[i];fac[i]=(ll)fac[i-1]*i%mod;}
	sort(b+1,b+n+1);
	tot=unique(b+1,b+n+1)-b-1;
	for(rg int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	input();
	for(rg int j=1;j<=n;j++)
	{
		t.clear();
		if(j==1) t.add(1,1);
		for(rg int i=1;i<=n;i++)
		{
			f[i][j]=t.query(a[i]);
			t.add(a[i],f[i][j-1]);
		}
	}
	for(rg int i=1;i<=n;i++)
	  for(rg int j=1;j<=n;j++)
	    g[i]=pls(g[i],f[j][i]);
	ans=g[n];
	for(rg int i=1;i<n;i++)
	{
		ans=pls(ans,(ll)g[i]*fac[n-i]%mod);
		ans=dec(ans,(ll)g[i+1]*(i+1)%mod*fac[n-i-1]%mod);
	}
	printf("%d\n",ans);
	return 0;
}

BZOJ4767 两双手

Problem

BZOJ

Solution

按照给出的两个基向量,把所有关键点拆成网格上的点。
然后计算的时候,每条不合法路径只在第一次经过关键点时计算即可。

随便怎么走

( i + j i ) \binom {i+j} i (ii+j)

但是禁止点不允许走。按拓扑序搞成一个序列

f [ i ] f[i] f[i]表示走到i个关键点且不经过其他中间的禁止点, g [ i ] [ j ] g[i][j] g[i][j]表示i到j的路径条数

f [ i ] = g [ 1 ] [ i ] − ∑ 1 < j < i f [ j ] ∗ g [ j ] [ i ] f[i]=g[1][i]-\sum_{1<j<i} f[j]*g[j][i] f[i]=g[1][i]1<j<if[j]g[j][i]

Code

#include 
#include 
#include 
#define rg register
using namespace std;
typedef long long ll;
const int maxn=510,maxm=500010,mod=1e9+7;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct vec{
	int x,y;
	bool operator < (const vec &b)const{return x==b.x?y<b.y:x<b.x;}
	int operator / (const vec &b)const
	{
		return x*b.y-y*b.x;
	}
}xi,yi,a[maxn];
int n,tot,fac[maxm],inv[maxm],f[maxn];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int trans(int &x,int &y)
{
	int up,down;vec tmp=(vec){x,y};
	up=tmp/xi;down=yi/xi;
	if(up%down) return 0;
	x=up/down;
	if(xi.x) y=(tmp.x-x*yi.x)/xi.x;
	else y=(tmp.y-x*yi.y)/xi.y;
	return 1;
}
int power(int x,int y)
{
	int res=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1)
	    res=(ll)res*x%mod;
	return res;
}
void input()
{
	int u,v,x,y;
	read(x);read(y);read(n);
	read(xi.x);read(xi.y);read(yi.x);read(yi.y);
	if(!trans(x,y)){puts("0");exit(0);}
	a[++tot]=(vec){0,0};
	for(rg int i=1;i<=n;i++)
	{
		read(u);read(v);
		if(trans(u,v))
		{
			if(0<=u&&u<=x&&0<=v&&v<=y) a[++tot]=(vec){u,v};
		}
	}
	a[++tot]=(vec){x,y};
	sort(a+1,a+tot+1);
	fac[0]=1;
	for(rg int i=1;i<maxm;i++) fac[i]=(ll)fac[i-1]*i%mod;
	inv[maxm-1]=power(fac[maxm-1],mod-2);
	for(rg int i=maxm-2;~i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod; 
}
inline int c(int n,int m){return m>n?0:(ll)fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline int calc(int i,int j)
{
	if(a[i].x>a[j].x||a[i].y>a[j].y) return 0;
	return c(a[j].x-a[i].x+a[j].y-a[i].y,a[j].x-a[i].x);
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	input();
	f[1]=1;
	for(rg int i=2;i<=tot;i++)
	{
		f[i]=calc(1,i);
		for(rg int j=2;j<i;j++) f[i]=dec(f[i],(ll)f[j]*calc(j,i)%mod);
	}
	printf("%d\n",f[tot]);
	return 0;
}

你可能感兴趣的:(=====动态规划=====,学习笔记,=====数学=====)