有关一类容斥问题

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/82949611

一类容斥问题

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

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

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

子集容斥: f [ s ] = t s ( 1 ) s t f [ t ] f[s]=\sum_{t\subset s}(-1)^{|s|-|t|}f[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)

相关题目

  • 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] 表示至少有i条边匹配上的方案数,那么我们就强制选i条边即可,其他的位置随便乱排。这样就至少有i个不合法的方案。

那我们就对搞下来的这个序列进行dp。记 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] 表示到第i个点选取了j条边且第i-1个点和第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)!

我们可以认同这个式子

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

移项即可得到

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

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

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

Code

#include <cstdio>
#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] 表示长度为i的lis的方案数。求法可以用dp得到,中间用BIT优化一下转移即可。

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

Code

#include <algorithm>
#include <cstring>
#include <cstdio>
#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;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/82949611