容斥的几种写法

容斥的几种写法

容斥公式本身就是 枚举出状态的组合,算其乘积,奇数个值为负,偶数个值为正

原理:

对于组合中每个状态有 不在两种,求其组合,可以用二进制枚举,也可以用递归

  1. 二进制
  2. 两层for循环
  3. 递归
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
#include<iomanip>
#include<map>
#include<vector>
#include<time.h>
#define mset(a,b)   memset(a,b,sizeof(a))
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=1e5;
const int branch=26;
const int inf=0x3f3f3f3f;
const int MOD=1e6+7;
//假设有tot个数,存储在num数组中
int num[20];//存放数字种类
int rc[maxn];//存放容斥的组合
int tot;//数字种类数
int cnt;//容斥组合的个数
void rc_for()//求出num中 tot个数的所有组合
{
    cnt=0;
    rc[cnt++]=1;//初始状态
    for(int i=0; i<tot; ++i)
    {
        int k=cnt;//下标为i的数与前k个数的所有组合的组合
        for(int j=0; j<k; ++j)
        {
            rc[cnt++]=num[i]*rc[j]*-1;//奇数个为负
        }
    }
}
void rc_bit()//用二进制来写
{
    int top=1<<tot;//共tot个数字
    cnt=0;
    for(int i=0; i<top; ++i)
    {
        int val=1;
        for(int k=0; k<tot; ++k) //枚举每个位是否为1 为1的组合整上去
        {
            if((1<<k)&i)
            {
                val*=-1*num[k];
            }
        }
        rc[cnt++]=val;
    }
}
void dfs(int k,int val)//正在枚举下标为k的状态, 之前组合乘积为-1
{
    if(k==tot)
    {
        rc[cnt++]=val;
        return ;
    }
    dfs(k+1,val*-1*num[k]);//下标为k的状态在组合中
    dfs(k+1,val);
}
void rc_dfs()
{
    cnt=0;
    dfs(0,1);//
}
int main()
{
    //主要是枚举这些数的所有组合(不能重复)
    /*
    测试
    */
    scanf("%d",&tot);
    for(int i=0; i<tot; ++i)
        scanf("%d",num+i);
    rc_for();
    printf("
rc_for.........
");
    cout<<"cnt:"<<cnt<<endl;
    for(int i=0; i<cnt; ++i)
        printf("%d ",rc[i]);
    puts("");
    printf("rc_bit.........
");
    rc_bit();
    cout<<"cnt:"<<cnt<<endl;
    for(int i=0; i<cnt; ++i)
        printf("%d ",rc[i]);
    puts("");
    rc_dfs();
    cout<<"cnt:"<<cnt<<endl;
    for(int i=0; i<cnt; ++i)
        printf("%d ",rc[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/dchnzlh/p/10427273.html