一类容斥问题
最近才接触到了一些dp最后容斥的题目,还经常和状压,概率期望啥的一起搞事情。由于之前基本没做过类似的题目,就被虐飞了,所以就做了一些题目。。
一般都是比如答案是要求“恰好…”,但是我们难以限制成恰好的情况,所以我们可以试图放宽条件,改成类似于“至少…”的状态。这个时候我们就可以先构造出满足至少的情况,其他的就可以随意组合。然后最后我们要统计答案的话就可以上容斥。注意先想好容斥的复杂度。。
还有一些比较迷的容斥模型,比如子集容斥,min-max容斥啊啥的,可能得要手推公式。随便丢点公式咯:
子集容斥:
min-max容斥:
相关题目
- AGC005D
- 51nod1518
- HDU4336
- BZOJ4036 按位或
- BZOJ4455
- BZOJ4767
- BZOJ4361
- BZOJ2560
- BZOJ4005
- BZOJ4596
- BZOJ3622
- BZOJ3294
- BZOJ2839
- BZOJ4710
- CF342D
- TC SRM498Div1 foxjump 11223
部分题解就直接丢在这下面了…如果有的话
AGC005D ~K Perm Counting
Problem
Solution
位置与值一共是2n个点,可以构造一个这样的二分图,如果我们在相差k的两点间连线。yy画一下这个图,那么这个图就相当于被拆成了几条链。我们把这些链放在一起考虑。
显然答案就是要求没有一条边匹配的方案数。然而这个并不好直接统计。所以我们不妨这样来考虑,设 表示至少有i条边匹配上的方案数,那么我们就强制选i条边即可,其他的位置随便乱排。这样就至少有i个不合法的方案。
那我们就对搞下来的这个序列进行dp。记 表示到第i个点选取了j条边且第i-1个点和第i个点之间的边有没有选时的方案数。记 表示选取了至少i条边的方案数,那么显然我们有 。
我们可以认同这个式子
移项即可得到
为什么这样是对的呢?我们推一推系数就会发现,其他每一项的系数相当于是n=i的组合数被做了一次奇偶性分组,并作差,显然除了 之外,其他的系数都为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
Solution
我们用 表示长度为i的lis的方案数。求法可以用dp得到,中间用BIT优化一下转移即可。
什么是不合法的方案?那就是最后得出来的lis是由另一个lis删除得到的。
考虑长度为i的lis的多余贡献,首先这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;
}