容斥专题三

本文总结了容斥原理在算法竞赛中的应用,通过多个题目详细解析了如何利用容斥原理解决组合计数、棋盘放置、圆环相交、石子跳跃等问题。文章介绍了如何优化复杂度,以及在不同场景下的解题思路,如利用高精度、同余方程、DFS搜索等方法。
摘要由CSDN通过智能技术生成

抱歉,抱歉,前两周忙着打网络赛,结果容斥专题断了整整两周。最后一个专题,总结性专题,现在继续。



第一题SGU-476

题意:3n个人,组成n个三人组,而且必须满足k组条件,每组条件表示a编号为a,b,c的三人不可以一个队。

分析:是容斥原理,用所有情况减去不能出现的情况,就是答案。对于不能出现的情况,对于条件的组数进行容斥,如果多组情况中有相同的成员编号那么就是不可能出现的,为0。这样的话,代码还是很好写的,而且用dfs的写法,还可以进行剪枝。

观察数据范围,发现答案很大,必须使用高精度。使用高精度打表的时候发现复杂度很高,预处理处理不出来,一个是高精度的模版不行(果断换掉),还有就是简化打表公式。对于没有条件的3n个人,方案数是 (3n)!6nn! ,知道通项,很方便的得到递推式 n0=1,ni=ni1(3n1)(3n2)2 ,这样打表很显然快很多。

但是这样的话,对于极限情况还是很慢,因为进行了多次大数相加(大约为1000的复杂度),递归深度是20,那么总的复杂度就是 1000220=109 ,极限情况是会TLE的。

之后,我考虑对于每一组具有相同有效条件数的搜索,它的方案数和符号都是一样的,那么我可以先将他们累积起来,再直接进行一次大数乘法和一次大数加法。这样的话,将时间复杂度优化到了10^6(常数可能有点大),就可以过了。

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          1000000+10
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}

//--------------------------------------------------
int compare(string str1,string str2)  
{  
    if(str1.length()>str2.length()) return 1;  
    else if(str1.length()<str2.length())  return -1;  
    else return str1.compare(str2);  
}  
string add(string str1,string str2)
{  
    string str;  

    int len1=str1.length();  
    int len2=str2.length();  
    if(len1<len2)  
    {  
        for(int i=1;i<=len2-len1;i++)  
           str1="0"+str1;  
    }  
    else  
    {  
        for(int i=1;i<=len1-len2;i++)  
           str2="0"+str2;  
    }  
    len1=str1.length();  
    int cf=0;  
    int temp;  
    for(int i=len1-1;i>=0;i--)  
    {  
        temp=str1[i]-'0'+str2[i]-'0'+cf;  
        cf=temp/10;  
        temp%=10;  
        str=char(temp+'0')+str;  
    }  
    if(cf!=0)  str=char(cf+'0')+str;  
    return str;  
}  
string sub(string str1,string str2)
{  
    string str;  
    int tmp=str1.length()-str2.length();  
    int cf=0;  
    for(int i=str2.length()-1;i>=0;i--)  
    {  
        if(str1[tmp+i]<str2[i]+cf)  
        {  
            str=char(str1[tmp+i]-str2[i]-cf+'0'+10)+str;  
            cf=1;  
        }  
        else  
        {  
            str=char(str1[tmp+i]-str2[i]-cf+'0')+str;  
            cf=0;  
        }  
    }  
    for(int i=tmp-1;i>=0;i--)  
    {  
        if(str1[i]-cf>='0')  
        {  
            str=char(str1[i]-cf)+str;  
            cf=0;  
        }  
        else  
        {  
            str=char(str1[i]-cf+10)+str;  
            cf=1;  
        }  
    }  
    str.erase(0,str.find_first_not_of('0')); 
    return str;  
}  
string mul(string str1,string str2)  
{  
    string str;  
    int len1=str1.length();  
    int len2=str2.length();  
    string tempstr;  
    for(int i=len2-1;i>=0;i--)  
    {  
        tempstr="";  
        int temp=str2[i]-'0';  
        int t=0;  
        int cf=0;  
        if(temp!=0)  
        {  
            for(int j=1;j<=len2-1-i;j++)  
              tempstr+="0";  
            for(int j=len1-1;j>=0;j--)  
            {  
                t=(temp*(str1[j]-'0')+cf)%10;  
                cf=(temp*(str1[j]-'0')+cf)/10;  
                tempstr=char(t+'0')+tempstr;  
            }  
            if(cf!=0) tempstr=char(cf+'0')+tempstr;  
        }  
        str=add(str,tempstr);  
    }  
    str.erase(0,str.find_first_not_of('0'));  
    return str;  
}  

void div(string str1,string str2,string &quotient,string &residue)  
{  
    quotient=residue="";
    if(str2=="0")
    {  
        quotient=residue="ERROR";  
        return;  
    }  
    if(str1=="0")
    {  
        quotient=residue="0";  
        return;  
    }  
    int res=compare(str1,str2);  
    if(res<0)  
    {  
        quotient="0";  
        residue=str1;  
        return;  
    }  
    else if(res==0)  
    {  
        quotient="1";  
        residue="0";  
        return;  
    }  
    else  
    {  
        int len1=str1.length();  
        int len2=str2.length();  
        string tempstr;  
        tempstr.append(str1,0,len2-1);  
        for(int i=len2-1;i<len1;i++)  
        {  
            tempstr=tempstr+str1[i];  
            tempstr.erase(0,tempstr.find_first_not_of('0'));  
            if(tempstr.empty())  
              tempstr="0";  
            for(char ch='9';ch>='0';ch--)
            {  
                string str,tmp;  
                str=str+ch;  
                tmp=mul(str2,str);  
                if(compare(tmp,tempstr)<=0) 
                {  
                    quotient=quotient+ch;  
                    tempstr=sub(tempstr,tmp);  
                    break;  
                }  
            }  
        }  
        residue=tempstr;  
    }  
    quotient.erase(0,quotient.find_first_not_of('0'));  
    if(quotient.empty()) quotient="0";  
}  
string dat[1000+10];
string ans,ant;
int tri[20+10][3];
int nun[20+10];
int vis[3000+10];
int n,k;
string Multiply(string s,int x)
{  
    reverse(s.begin(),s.end());  
    int cmp=0;  
    for(int i=0;i<s.size();i++)  
    {  
        cmp=(s[i]-'0')*x+cmp;  
        s[i]=(cmp%10+'0');  
        cmp/=10;  
    }  
    while(cmp)  
    {  
        s+=(cmp%10+'0');  
        cmp/=10;  
    }  
    reverse(s.begin(),s.end());  
    s.erase(0,s.find_first_not_of('0'));
    return s;  
}  
void init(){
    dat[0]="1";
    for(int i=1;i<=1000;i++)
        dat[i]=Multiply(dat[i-1],(3*i-2)*(3*i-1)/2);
}
void dfs(int len,int num){
    if(len==k){
        if(num)nun[num]++;
        return ;
    }
    dfs(len+1,num);
    for(int i=0;i<3;i++)
        if(vis[tri[len][i]])return ;
    for(int i=0;i<3;i++)
        vis[tri[len][i]]=1;
    dfs(len+1,num+1);
    for(int i=0;i<3;i++)
        vis[tri[len][i]]=0;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    init();
    while(~scanf("%d %d",&n,&k)){
        ans=dat[n];
        ant="0";
        for(int i=0;i<k;i++)
            for(int j=0;j<3;j++)
                scanf("%d",&tri[i][j]);
        clr(vis,0);
        clr(nun,0);
        dfs(0,0);
        for(int i=1;i<=k;i++)
            if(nun[i]){
                if(i&1)ant=add(ant,Multiply(dat[n-i],nun[i]));
                else ans=add(ans,Multiply(dat[n-i],nun[i]));
            }
        string anw=sub(ans,ant);
        if(anw.size())cout<<sub(ans,ant)<<endl;
        else puts("0");
    }
    return 0;
}


第二题uva-10325

分析:容斥的简单题目,看前面的专题。某个区间中不能被集合连任何一个数整除的数的个数?

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          300+10
#define   SIZE          10000+5
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   clr(x,y)      memset(x,y,sizeof(x))
#define   rep(i,n)      for(int i=0;i<(n);i++)
#define   repf(i,a,b)   for(int i=(a);i<=(b);i++)
#define   pii           pair<int,int>
#define   mp            make_pair
#define   FI            first
#define   SE            second
#define   IT            iterator
#define   PB            push_back
#define   Times         10

typedef   long long     ll;
typedef   unsigned long long ull;
typedef   long double   ld;

const double eps   = 1e-10;
const double  pi   = acos(-1.0);
const  ll    mod   = 1e9+7;
const  int   inf   = 0x3f3f3f3f;
const  ll    INF   = (ll)1e18+300;
const double delta = 0.98;

inline void RI(int& x)
{
    x=0;
    char c=getchar();
    while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
    bool flag=1;
    if(c=='-')
    {
        flag=0;
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}

//--------------------------------------------------

int dat[20];
int n,m;
ll lcm(ll a,ll b){
    return a/__gcd(a,b)*b;
}
ll solve(){
    ll ans=0;
    for(int i=1;i<(1<<m);i++){
        ll t=1,num=0;
        for(int j=0;j<m;j++)
            if(i&(1<<j)){
                t=lcm(t,dat[j]);
                num++;
            }
        if(num&1)ans+=n/t;
        else ans-=n/t;
    }
    return n-ans;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    while(~scanf("%d %d",&n,&m)){
        for(int i=0;i<m;i++)
            s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值