容斥小结

本篇小结大部分都是广义容斥原理,你可以看看这篇容斥原理小结,shadow;

bzoj3622
  • 考虑糖果比药片大的对数比药片比糖果大的对数不太好考虑,我们先只考虑糖果比药片多的对数
  • 显然先排序,对每个糖果处理一个\(num\)表示有\(num\)个药片它小,设\(dp[i][j]\)表示考虑前i个,有j对符合条件,转移方程\[dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*max(0,num[i]-j+1)\],第一项代表不选第i个,第二项代表选,前面有\(max(0,num[i]-j+1)\)个可以与他配对
  • 然后令\(ans[i]=dp[n][i]*(n-i)!\),没有配对的就直接阶乘乱放
  • 显然会算重,直接二项式定理容斥就行了
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=2000+5;
bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;}
bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
  T ans=0,f=1;
  char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
  return ans*f;
}
template<typename T>void write(T x,char y)
{
  if(x==0)
  {
      putchar('0');putchar(y);
      return;
  }
  if(x<0)
  {
      putchar('-');
      x=-x;
  }
  static char wr[20];
  int top=0;
  for(;x;x/=10)wr[++top]=x%10+'0';
  while(top)putchar(wr[top--]);
  putchar(y);
}
void file()
{
  #ifndef ONLINE_JUDGE
      freopen("3622.in","r",stdin);
      freopen("3622.out","w",stdout);
  #endif
}
const int mo=1e9+9;
int n,k;
int a[N],b[N];
void input()
{
    n=read<int>(),k=read<int>();
    k=(n+k)>>1;
    For(i,1,n)a[i]=read<int>();
    For(i,1,n)b[i]=read<int>();
}
int num[N],dp[N][N],ans[N];
void add(int &a,int b)
{
    a+=b;if(a>=mo)a-=mo;
}
int C[N][N],mc[N];
void init(int maxn)
{
    For(i,0,maxn)C[i][0]=C[i][i]=1;
    For(i,2,maxn)For(j,1,i-1)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
    mc[0]=1;For(i,1,maxn)mc[i]=1ll*mc[i-1]*i%mo;
}
void work()
{
    int pos=0;
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    For(i,1,n)
    {
        while(pos<n&&b[pos+1]<a[i])++pos;
        num[i]=pos;
        
    }
    dp[0][0]=1;
    For(i,1,n)For(j,0,i)
    {
        dp[i][j]=dp[i-1][j];
        if(j)add(dp[i][j],1ll*dp[i-1][j-1]*max(num[i]-j+1,0)%mo);
    }
    Fordown(i,n,k)
    {
        ans[i]=1ll*dp[n][i]*mc[n-i]%mo;
        For(j,i+1,n)add(ans[i],mo-1ll*ans[j]*C[j][i]%mo);
    }
    write(ans[k],'\n');
}
int main()
{
    file();
    input();
    init(max(n,k));
    work();
    return 0;
}
bzoj4361
  • 先设\(dp[i][j]\)表示到第i位,最长不降子序列长为j的方案数,本来是\(n^2\)的,用\(BIT\)优化一下
  • \(f[i]\)表示最长非降子序列长为j的方案之和,\(f[i]=\sum_{j=i}^{n}dp[j][i]*(j-i)!\),删去的元素顺序随意
  • 算重,因为有些数列删到\(i+1\)时就已经满足要求,但我们还是把他删到了\(i\),所以容斥为\(ans=\sum_{i=n}^{1}f[i]*(n-i)!-f[i+1]*(n-i-1)!*(i+1)\),就是枚举是\(i+1\)的方案中删掉了哪一个数而非法的
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=2e3+5;
bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;}
bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
  T ans=0,f=1;
  char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
  return ans*f;
}
template<typename T>void write(T x,char y)
{
  if(x==0)
  {
      putchar('0');putchar(y);
      return;
  }
  if(x<0)
  {
      putchar('-');
      x=-x;
  }
  static char wr[20];
  int top=0;
  for(;x;x/=10)wr[++top]=x%10+'0';
  while(top)putchar(wr[top--]);
  putchar(y);
}
void file()
{
  #ifndef ONLINE_JUDGE
      freopen("4361.in","r",stdin);
      freopen("4361.out","w",stdout);
  #endif
}
int n,a[N];
void input()
{
    n=read<int>();
    For(i,1,n)a[i]=read<int>();
}
const int mo=1e9+7;
int dp[N][N];
void add(int &x,int y)
{
    x+=y;if(x>=mo)x-=mo;
}
struct BIT
{
    int sum[N];
    void update(int x,int v)
    {
        for(;x<=n;x+=x&-x)add(sum[x],v);
    }
    int cal(int x)
    {
        int res=0;
        for(;x;x-=x&-x)add(res,sum[x]);
        return res;
    }
}s[N];
int mc[N],ans;
int f[N],q[N],top;
void init()
{
    For(i,1,n)q[i]=a[i];
    sort(q+1,q+n+1);
    top=unique(q+1,q+n+1)-q-1;
    For(i,1,n)a[i]=lower_bound(q+1,q+top+1,a[i])-q;
    mc[0]=1;For(i,1,n)mc[i]=1ll*mc[i-1]*i%mo;
}
void work()
{
    For(i,1,n)
    {
        Fordown(j,n,2)
        {
            add(dp[i][j],s[j-1].cal(a[i]));
            s[j].update(a[i],dp[i][j]);
            add(f[j],dp[i][j]);
        }
        dp[i][1]=1;s[1].update(a[i],dp[i][1]);
        f[1]++;
    }
    //For(i,1,n)For(j,1,n)write(dp[i][j],j==n?'\n':' ');
    Fordown(i,n,1)
    {
        add(ans,1ll*f[i]*mc[n-i]%mo);
        add(ans,mo-1ll*f[i+1]*mc[n-i-1]%mo*(i+1)%mo);
    }
    write(ans,'\n');
}
int main()
{
    file();
    input();
    init();
    work();
    return 0;
}
bzoj4710
  • 一开始疯狂考虑同一种特产算重的情况,思路完全错误了
  • 因为所有人都要有,所以我们枚举有几个人分到了特产,设\(f[i]\)表示有i个人分到了特产,那么\(f[i]=\prod_{j=1}^{m}C[a[j]+i-1][i-1]\),然后直接二项式容斥就行了
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=1000+5;
template<typename T>bool cmax(T &a,T b){return (a<b)?a=b,1:0;}
template<typename T>bool cmin(T &a,T b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
  T ans=0,f=1;
  char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
  return ans*f;
}
template<typename T>void write(T x,char y)
{
  if(x==0)
  {
      putchar('0');putchar(y);
      return;
  }
  if(x<0)
  {
      putchar('-');
      x=-x;
  }
  static char wr[20];
  int top=0;
  for(;x;x/=10)wr[++top]=x%10+'0';
  while(top)putchar(wr[top--]);
  putchar(y);
}
void file()
{
  #ifndef ONLINE_JUDGE
      freopen("4710.in","r",stdin);
      freopen("4710.out","w",stdout);
  #endif
}
const int mo=1e9+7;
int n,m,a[N];
void input()
{
    n=read<int>(),m=read<int>();
    For(i,1,m)a[i]=read<int>();
}
int ans;
int C[2005][2005];
const int maxn=2e3;
void work()
{
    For(i,0,maxn)C[i][0]=C[i][i]=1;
    For(i,2,maxn)For(j,1,i-1)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
    int sum;
    For(i,1,n)
    {
        sum=1;
        For(j,1,m)sum=1ll*sum*C[a[j]+i-1][i-1]%mo;
        if((n-i)&1)ans=(ans-1ll*sum*C[n][i]%mo+mo)%mo;
        else ans=(ans+1ll*sum*C[n][i]%mo)%mo;
    }
    write(ans,'\n');
}
int main()
{
    file();
    input();
    work();
    return 0;
}
bzoj
  • \(f[i]\)强制选择i个相同元素的方案数,\(f[i]={a\choose b}*(2^{2^{n-i}}-1)\),注意计算上面指数要\(\%(mod-1)\),(WA了半天),剩下就是个二项式容斥了
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=1e6+5;
template<typename T>bool cmax(T &a,T b){return (a<b)?a=b,1:0;}
template<typename T>bool cmin(T &a,T b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
  T ans=0,f=1;
  char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
  return ans*f;
}
template<typename T>void write(T x,char y)
{
  if(x==0)
  {
      putchar('0');putchar(y);
      return;
  }
  if(x<0)
  {
      putchar('-');
      x=-x;
  }
  static char wr[20];
  int top=0;
  for(;x;x/=10)wr[++top]=x%10+'0';
  while(top)putchar(wr[top--]);
  putchar(y);
}
void file()
{
  #ifndef ONLINE_JUDGE
      freopen("2839.in","r",stdin);
      freopen("2839.out","w",stdout);
  #endif
}
const int mo=1e9+7;
int n,k;
void input()
{
    n=read<int>(),k=read<int>();
}
int pw[N],f[N],mc[N],inv[N];
ll power(ll x,int y)
{
    ll res=1;
    for(;y;x=x*x%mo,y>>=1)if(y&1)res=res*x%mo;
    return res;
}
int C(int n,int m)
{
    return 1ll*mc[n]*inv[m]%mo*inv[n-m]%mo;
}
void work()
{
    pw[0]=1;For(i,1,n)pw[i]=1ll*(pw[i-1]<<1)%mo;
    For(i,1,n)pw[i]=power(2,pw[i])-1;
    mc[0]=1;For(i,1,n)mc[i]=1ll*mc[i-1]*i%mo;
    inv[0]=1;inv[n]=power(mc[n],mo-2);
    Fordown(i,n-1,1)inv[i]=1ll*inv[i+1]*(i+1)%mo;
    For(i,k,n)f[i]=1ll*C(n,i)*pw[n-i]%mo;
    For(i,k+1,n)f[k]=(f[k]+1ll*f[i]*C(i,k)%mo*(((i-k)&1)?-1:1)%mo+mo)%mo;
    write(f[k],'\n');
}
int main()
{
    file();
    input();
    work();
    return 0;
}
bzoj 4767
  • 首先解个方程,两个解就是两种方式所需要的步数,对于每个障碍点也算出两种方式所需要的步数,然后将解排序,用每个点的方案减去前面所有可以到达它的障碍点到它的方案即可。
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=500+5;
template<typename T>bool cmax(T &a,T b){return (a<b)?a=b,1:0;}
template<typename T>bool cmin(T &a,T b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
  T ans=0,f=1;
  char ch=getchar();
  while(!isdigit(ch)&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();
  while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
  return ans*f;
}
template<typename T>void write(T x,char y)
{
  if(x==0)
  {
      putchar('0');putchar(y);
      return;
  }
  if(x<0)
  {
      putchar('-');
      x=-x;
  }
  static char wr[20];
  int top=0;
  for(;x;x/=10)wr[++top]=x%10+'0';
  while(top)putchar(wr[top--]);
  putchar(y);
}
void file()
{
  #ifndef ONLINE_JUDGE
      freopen("4767.in","r",stdin);
      freopen("4767.out","w",stdout);
  #endif
}
int A,B,n;
int ax,bx,ay,by;
void input()
{
    A=read<int>(),B=read<int>();n=read<int>();
    ax=read<int>(),ay=read<int>(),bx=read<int>(),by=read<int>();
}
void cal(int x1,int y1,int x2,int y2,int &t1,int &t2)
{
    int x=x2-x1,y=y2-y1;
    int temp=x*by-y*bx;
    int res=by*ax-ay*bx;
    if(temp%res!=0)t1=t2=-1;
    else
    {
        t1=temp/res;
        if(bx)
        {
            temp=x-ax*t1;
            if(temp%bx==0)t2=temp/bx;
            else t1=t2=-1;
        }
        else if(by)
        {
            temp=y-ay*t1;
            if(temp%by==0)t2=temp/by;
            else t1=t2=-1;
        }
    }
}
const int mo=1e9+7;
const int maxn=5e5;
int inv[maxn+5],mc[maxn+5];
ll power(ll x,int y)
{
    ll res=1;
    for(;y;x=x*x%mo,y>>=1)if(y&1)res=res*x%mo;
    return res;
}
void init()
{
    mc[0]=1;For(i,1,maxn)mc[i]=1ll*mc[i-1]*i%mo;
    inv[0]=1;inv[maxn]=power(mc[maxn],mo-2);
    Fordown(i,maxn-1,1)inv[i]=1ll*inv[i+1]*(i+1)%mo;
}
int C(int x,int y)
{
    if(x<y)return 0;
    return 1ll*mc[x]*inv[y]%mo*inv[x-y]%mo; 
}
int ans;
typedef pair<int,int> pii;
pii q[N];
int tot;
#define mk make_pair
#define fir first
#define sec second
void add(int &a,int b)
{
    a+=b;if(a>=mo)a-=mo;
}
int path(int i,int j)
{
    int x=q[j].fir-q[i].fir ,y=q[j].sec-q[i].sec;
    if(x<0||y<0)return 0;
    return C(x+y,y);
}
int dp[N];
void work()
{
    int t1,t2;
    cal(0,0,A,B,t1,t2);
    if(t1<0||t2<0){puts("0");return;}
    A=t1,B=t2;
    q[++tot]=mk(t1,t2);
    int x,y;
    For(i,1,n)
    {
        x=read<int>(),y=read<int>();
        cal(0,0,x,y,t1,t2);
        if(t1>=0&&t2>=0&&t1<=A&&t2<=B)q[++tot]=mk(t1,t2);
    }
    sort(q+1,q+tot+1);
    For(i,1,tot)
    {
        dp[i]=C(q[i].fir+q[i].sec,q[i].fir);
        For(j,1,i-1)add(dp[i],mo-1ll*dp[j]*path(j,i)%mo);
    }
    write(dp[tot],'\n');
}
int main()
{
    file();
    input();
    init();
    work();
    return 0;
}

转载于:https://www.cnblogs.com/dengyixuan/p/8818624.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值