容斥原理专题一

抱歉,很久没有更新博客了。

这几天集中刷了容斥原理的题目,于是就来写博客巩固下。容斥原理,我想大家在高中都或多或少的学过。虽然知道原理内容,但是用来解题的话,还是有点小障碍的,特别是不知道怎么写代码。

如果读者连最基本的容斥原理都不理解的话,或者理解不深入、不知道容斥原理用来解决什么问题的话,请下载这篇PDF详细研读,相信会有收获:http://pan.baidu.com/s/1hrISIjy 密码: varw

容斥原理的常见代码写法一般有两种:位运算法和DFS法。简单比较一下,位运算法在写法上比较简单,而且非常好理解,利用二进制位的遍历能够无遗漏地找到所有集合,但是缺点也很明显,基本没有办法剪枝,如果遇到TLE的话,请立刻改成DFS法,并进行某种剪枝操作。DFS法,正好与位运算法相反,它不是很好写而且可能需要某种程度的剪枝,但是DFS可以处理大一点的数据,速度上要比位运算法好一点。



第一题 hdu-2204

分析:首先,我们可以简单的发现从1到n的这些数中能表示成M^2的形式的数有floor(pow(n,1.0/2.0))个,能表示成M^3的形式的数有floor(pow(n,1.0/3.0))个,但是能表示成M^4的形式的数一定可以表示成(m^2)^2的形式,我们已经在M^2的那种里面算过了,所以我们可以发现,只有幂指数K为素数时,形式为M^K的个数才需要被计算,其他合数的情况是不需要计算的。但是还是出现了一个问题,那就是比如64的情况,64可以表示为8^2,也可以表示为4^3,那这样的话,M^2和M^3都算过了一次64,那么就重复了,这还仅仅是2和3的情况,如果多个素数乘在一起的情况,就分不清到底是该加还是该减。那么这就需要用到容斥原理,这里我们要求的其实是能够表示成M^2或者M^3或者M^5或者M^7或者M^11或者。。。。。。(一直到floor(pow(n,1.0/k))==1的情况为止),那么利用容斥原理就十分的好求了。

这道题精度有点难控制,需要单独写一个开高次方的函数(测试数据非常的弱,大数据估计只有1000000000000000000一个,如果你不高兴写这个函数,那么你直接特判一下10^18就行了)而且,这里有个剪枝的小技巧,当开方的数大于100时,根本不需要开方,因为开方的结果肯定是1,想一想为什么呢?

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          100+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
#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;

/*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;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

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

int tot;
bool isprime[maxn];
int prime[maxn];
void init(int n){
    for(int i=2;i<=n;i++){
        if(!isprime[i])
            prime[tot++]=i;
        for(int j=0;prime[j]*i<=n;j++){
            isprime[prime[j]*i]=true;
            if(i%prime[j]==0)break;
        }
    }
}
ll qlow(ll a,ll n){
    ll ans=1;
    while(n){
        if(n&1){
            double t=1.0*INF/ans;
            if(treturn -1;
            ans=ans*a;
        }
        n>>=1;
        if(a>(1ll<<31)&&n>0)return -1;
        a=a*a;
    }
    return ans;
}
ll cal(ll n,ll k){
    ll ans=(ll)pow(n,1.0/k);
    ll tmp=qlow(ans,k);
    if(tmp==n)return ans;
    if(tmp>n||tmp==-1)ans--;
    else {
        tmp=qlow(ans+1,k);
        if(tmp<=n&&tmp!=-1)ans++;
    }
    return ans;
}
ll solve(ll n){
    vector<int>p;
    for(int i=0;iif(t==1)break;
        p.PB(prime[i]);
    }
    ll ans=0;
    for(int i=1;i<(1ll<1,num=0;
        for(int j=0;jif(i&(1<if(num>4||mult>100)t=1;
        else t=cal(n,mult);
        if(num&1)ans+=t;
        else ans-=t;
    }
    return ans;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    init(100);
    ll n;
    while(~scanf("%lld",&n)){
        if(n<=3)puts("1");
        else printf("%lld\n",solve(n));
    }
    return 0;
}



第二题 hdu-3208

分析:这道题看上去和上一道题很类似,但是解法上改变还是很多的。

首先,这道题目是从a到b,那么我们改变胰腺癌套路,不妨用solve(n)这个函数解决从1到n的情况,然后将solve(b)-solve(a-1)就从a到b的情况。再看看题意上的改变,上一题是能表示成M^K(K>1)的数的个数,这里是每个数都可以表示成M^K(K>=1)的形式,而且K作为这个数的权值加到答案中,我们可以借鉴前面的经验。最简单的办法就是枚举每个K,单独算出权值为K的时候有多少个数,这样只要乘一下就可以累加到答案中了。我们发现这次就不是按照素数来看了,而是从2到3到4到。。。一直到(floor(pow(n,1.0/K))==1)为止。那么我们怎么算出仅能表示成M^K的形式的数的个数呢。首先我们将N开K次方求出,(M)^K中的M有多少个,这些是有可能成为的,但是其中夹杂着很多不是的。怎么判断那些不是的呢,这里观察这些不是的1,2,3。。。,M,其中如果有I能够表示成P^Q(Q>1)的话,那么显然,这个就不是,因为I=P^Q,那么这个原数就是(P^Q)^K,那么其实它是可以表示成P^(QK)的,所以他的权值不是K,而是QK(Q>1)。这样的话相当于求前一道题的逆命题,那当然非常好求。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn             200+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
#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;

/*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;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

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

int tot=0;
bool isprime[maxn];
int prime[maxn];
void init(int n){
    for(int i=2;i<=n;i++){
        if(!isprime[i])
            prime[tot++]=i;
        for(int j=0;1ll*prime[j]*i<=n;j++){
            isprime[prime[j]*i]=true;
            if(i%prime[j]==0)break;
        }
    }
}
ll qlow(ll a,ll n){
    ll ans=1;
    while(n){
        if(n&1){
            double t=1.0*(INF)/ans;
            if(treturn -1;
            ans=ans*a;
        }
        n>>=1;
        if(a>(1ll<<31)&&n>0)return -1;
        a=a*a;
    }
    return ans;
}
ll cal(ll n,ll k){
    ll ans=(ll)pow(n,1.0/k);
    ll tmp=qlow(ans,k);
    if(tmp==n)return ans;
    if(tmp>n||tmp==-1)ans--;
    else {
        tmp=qlow(ans+1,k);
        if(tmp<=n&&tmp!=-1)ans++;
    }
    return ans;
}
ll calc(ll n){
    if(n<=3)return n-1;
    vector<int>p;
    for(int i=0;iif(tmp==1)break;
        p.PB(prime[i]);
    }
    ll ans=0;
    for(int i=1;i<(1ll<1,num=0;
        for(int j=0;jif(i&(1<if(num&1)ans+=cal(n,mult);
        else ans-=cal(n,mult);
    }
    return n-ans;
}
ll solve(ll n){
    if(n==1)return 0;
    ll ans=0,num=n-1;
    for(int i=2;1;i++){
        ll tt=cal(n,i);
        if(tt==1)break;
        ll tmp=calc(tt);
        ans+=i*tmp;
        num-=tmp;
    }
    return ans+num;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    ll a,b;
    init(200);
    while(scanf("%lld %lld",&a,&b),a||b){
        printf("%lld\n",solve(b)-solve(a-1));
    }
    return 0;
}


第三题 hdu-1796

分析:这是一道很明显的容斥问题,而且在PDF应用例题的第六题里面有详细说明,我这里就不赘述了。

这道题目注意下0,非常坑。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn             200+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
#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;

/*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;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

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

int dat[30];
int cal(int a,int b){
    int t=__gcd(a,b);
    return a/t*b;
}
int solve(int n,int m){
    int ans=0;
    for(int i=1;i<(1ll<int mult=1,num=0;
        for(int j=0;jif(i&(1<if(num&1)ans+=n/mult;
        else ans-=n/mult;
    }
    return ans;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int n,m;
    while(~scanf("%d %d",&n,&m)){
        for(int i=0;iscanf("%d",&dat[i]);
            if(dat[i]==0){
                m--;i--;
            }
        }
        printf("%d\n",solve(n-1,m));
    }
    return 0;
}


第四题 hdu-2841

分析:这道题以前用莫比乌斯反演求过,但是这次用容斥原理求。哦!忘了说了凡是用莫比乌斯反演可做的题目一定可以用容斥原理来做。

转换一下等价题意:有两个数x和y,其中1<=x<=n,1<=y<=m,问有多少对有序对(x,y)能使得gcd(x,y)=1。

说句实话,莫比乌斯反演什么早就忘光了,不过不要紧,容斥原理比它更厉害。

这里就是用最简单的思路,枚举x求与x互质的y的个数,然后一个一个的加,速度比莫比乌斯慢那倒是真的。

如果不知道怎么求a到b之间与n互质的数的个数,那么请去看看PDF的应用例题第五题,上面有详细的介绍和相应的代码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn             100000+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
#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;

/*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;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

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

int solve(int r,int n){
    vectorp;
    for(int i=2;1ll*i*i<=n;i++)
        if(n%i==0){
            p.PB(i);
            while(n%i==0)
                n/=i;
        }
    if(n>1)p.PB(n);
    int ans=0;
    for(int i=1;i<(1<int mult=1,num=0;
        for(int j=0;jif(i&(1<if(num&1)ans+=r/mult;
        else ans-=r/mult;
    }
    return r-ans;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d %d",&n,&m);
        if(n>m)swap(n,m);
        ll ans=m;
        for(int i=2;i<=n;i++)
            ans+=solve(m,i);
        printf("%lld\n",ans);
    }
    return 0;
}


第五题 hdu-1695

分析:与上一题基本没有什么区别,转换一下等价题意:有两个数x和y,其中1<=x<=n,1<=y<=m,问有多少对无序对(x,y)能使得gcd(x,y)=1。

很简单了,用上面的方法把算了两次的减一减就行了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn             100000+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
#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;

/*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;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

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

int solve(int r,int n){
    if(n==1)return r;
    vector<int>p;
    for(int i=2;1ll*i*i<=n;i++)
        if(n%i==0){
            p.PB(i);
            while(n%i==0)
                n/=i;
        }
    if(n>1)p.PB(n);
    ll ans=0;
    for(int i=1;i<(1<int mult=1,num=0;
        for(int j=0;jif(i&(1<if(num&1)ans+=r/mult;
        else ans-=r/mult;
    }
    return r-ans;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        int a,b,c,d,k;
        scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
        if(k==0){
            printf("Case %d: 0\n",cas);
            continue;
        }
        a=b/k;b=d/k;
        if(a==0||b==0){
            printf("Case %d: 0\n",cas);
            continue;
        }
        ll ans=1;
        if(a>b)swap(a,b);
        for(int i=1;i<=a;i++)
            ans+=solve(b,i)-solve(i,i);
        printf("Case %d: %lld\n",cas,ans);
    }
    return 0;
}


第六题 zoj-2836

分析:因为zoj已经挂了快两个多月了,题目没法交,所以代码我也不敢贴(万一错了,不是自己打自己脸),就说分析把。

其实也没什么好分析的,就是和上面第三题一个套路。



第七题 zoj-3233

分析:看上去有点像水题的非水题。转换等价题意:给你数集A和数集B(每个数集里面有若干个数),让你求从a到b之间有多少个数满足能被某个Ai整除并且能被某个Bi整除。是不是非常绕,换句话说就是,这个数能被某个Ai整除,但是所有Bi都不能整除这个数。思路还是比较好想的:先求出所有能被至少一个Ai整除的数的个数,然后求所有Bi的lcm最小公倍数BB,求出所有能被至少一个Ai整除而且一定能被BB整除的数的个数,有人会问,那这个怎么求啊。很简单,将每个Ai与BB求最小公倍数Ci,那么就是求所有能被至少一个Ci整除的数的个数,最后两个答案减一减。

根据上面的原因代码也不敢贴。



第八题 ural-1036

分析:这是一道数学题。说说大致思路把,也是用到的容斥原理。

先转换一下等价题意:2n位编码(可以前导0,但是每位只能在0到9之间),前n位数字之和与后n为数字之和相同,而且总的数字之和是S,问有多少种不同的编码。那么很好想的是,先将s除以2(当然除不尽肯定方案数为0),看看s/2在n上有多少种方案,然后平方一下即可。问题转化为x1+x2+x3+…+xn=s/2且0<=xi<=9的方程解的个数。那么,我们参考PDF里面应用例题里面的第3题还是第4题,得到答案C(S/2+n-1,n-1)-C(n,1)C(S/2+n-1-10,n-1)+C(n,2)C(S/2+n-1-20,n-1)-C(n,3)C(S/2+n-1-30,n-1)。。。一直加到S/2+n-1-10*i

哦,对了,最后结果很大,记得用高精度模版!

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          500+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
#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;

/*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;
        c=getchar();
    }
    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()return -1;  
    else return str1.compare(str2);  
}  
string add(string str1,string str2)
{  
    string str;  

    int len1=str1.length();  
    int len2=str2.length();  
    if(len1for(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]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;  
}
string dat[maxn][maxn];
void init(){
    for(int i=0;i<=500;i++)
        dat[i][0]="1",dat[i][i]="1";
    for(int i=2;i<=500;i++)
        for(int j=1;jif(j<=i/2)dat[i][j]=add(dat[i-1][j],dat[i-1][j-1]);
            else dat[i][j]=dat[i][i-j];
        }
}
string C(int n,int m){
    return dat[n][m];
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    int n,s;
    init();
    while(~scanf("%d %d",&n,&s)){
        if((s&1)||s/2>n*9){
            puts("0");
            continue;
        }
        s>>=1;
        string ans=C(s+n-1,n-1);
        string ant="0";
        for(int i=1;s-10*i>=0;i++){
            string t=mul(C(n,i),C(s+n-1-10*i,n-1));
            if(i&1)ant=add(ant,t);
            else ans=add(ans,t);
        }
        ans=sub(ans,ant);
        cout<return 0;
}


第九题 ural-1091

分析:转换一下等价题意先:从1到s中找一组数(每组数必须要K个,而且两两不能相等),问有多少组能够使得这组数的gcd大于1。既然gcd大于1,那么我们不妨枚举gcd:当gcd为2时,我们求出(1到s中)2的倍数的个数,从中取k个(组合数);当gcd为3的时候,我们求出3的倍数的个数,从中取k个;当gcd为4时,我们发现这种情况被包括在了gcd为2的情况里面,那么我们就不自找麻烦了,就不用算了。然后我们显然的发现又是枚举素数,一直枚举到小于s的最大素数。但是,我们又发现当gcd为6的情况,其实被2和3算了两遍,依此类推,又是按照素数来容斥。就是求gcd为2或者gcd为3或者gcd为5或者。。。的情况。

对了事先打好素数表和组合数表比较方便。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          100+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
#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;

/*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;
        c=getchar();
    }
    while(c<='9'&&c>='0')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    if(!flag)x=-x;
}*/

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

int tot=0;
bool isprime[maxn];
int prime[maxn];
ll dat[maxn][maxn];
void init(int n){
    for(int i=2;i<=n;i++){
        if(!isprime[i])
            prime[tot++]=i;
        for(int j=0;1ll*prime[j]*i<=n;j++){
            isprime[prime[j]*i]=true;
            if(i%prime[j]==0)break;
        }
    }
    for(int i=0;i<=50;i++)
        dat[i][0]=dat[i][i]=1;
    for(int i=2;i<=50;i++)
        for(int j=1;jif(j<=i/2)dat[i][j]=dat[i-1][j]+dat[i-1][j-1];
            else dat[i][j]=dat[i][i-j];
}
int solve(int k,int s){
    vector<int>p;
    for(int i=0;iint t=s/prime[i];
        if(tbreak;
        p.PB(prime[i]);
    }
    ll ans=0;
    for(int i=1;i<(1ll<1,num=0;
        for(int j=0;jif(i&(1<int t=s/mult;
        if(t>=k){
            if(num&1)ans+=dat[t][k];
            else ans-=dat[t][k];
        }
    }
    if(ans>10000)return 10000;
    else return ans;
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    init(100);
    int k,s;
    while(~scanf("%d %d",&k,&s)){
        printf("%d\n",solve(k,s));
    }
    return 0;
}


第十题 ural-1114

分析:转换一下等价题意先:n个(带编号)盒子,a个(一模一样)红球,b个(一模一样)蓝球,问将所有球(不需要全部放完)放到盒子里面的种数。

那么显然红球和蓝球是可以分开来求解,然后乘起来。比如红球有a个,求放红球的方案数,那么就是进一步转换为方程0<=x1+x2+x3+..+xn<=a的解的个数,根据组合数学里面很简单的结论得到C(n-1+a,n-1)+C(n-1+a-1,n-1)+C(n-1+a-2,n-1)+…+C(n-1,n-1),然后化简一下是。。。哎呀,就这么算的,化简多麻烦啊,组合恒等式太多根本记不得。结果很大,高精度上!

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;
#define   maxn          40+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
#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;

/*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;
        c=getchar();
    }
    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()return -1;  
    else return str1.compare(str2);  
}  
string add(string str1,string str2)
{  
    string str;  

    int len1=str1.length();  
    int len2=str2.length();  

    if(len1for(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 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;  
}
string dat[maxn][maxn];
string C[maxn][maxn];
void init(){
    for(int i=0;i<=35;i++)
        C[i][0]="1",C[i][i]="1";
    for(int i=2;i<=35;i++)
        for(int j=1;jif(j<=i/2)C[i][j]=add(C[i-1][j],C[i-1][j-1]);
            else C[i][j]=C[i][i-j];
    for(int i=1;i<=20;i++){
        dat[i][0]="1";
        for(int j=1;j<=15;j++)
            dat[i][j]=add(dat[i][j-1],C[i+j-1][i-1]);
    }
}
int main(){
    //freopen("d:\\acm\\in.in","r",stdin);
    init();
    int n,a,b;
    while(~scanf("%d %d %d",&n,&a,&b)){
        cout<return 0;
}

累死我了,从6点开始写的这篇专题,一直写到9点半,图书馆都快关门了。而且还只写了十道题(里面还偷懒了几道),后面大约还有二十道题左右!请期待专题二与三!

你可能感兴趣的:(数学)