【题目整理】容斥定理+鸽巢原理

T^TOJ2332 电灯泡(容斥定理)

【题意】

V_Dragon有n栈电灯泡,编号为1-n,每个灯泡都有一个开关。那么问题来了

  1. 所有灯泡初始时为不亮的

  2. V_Dragon分别进行三次操作

  3. 每次操作他都选一个质数x,将编号为x和x的整数倍的灯泡的开关都拨动一下(如果灯为亮,那么拨动以后灯为不亮,如果灯不亮,拨动以后变为亮)

求最后亮着的灯的数量

【解题思路】

画一下图可以知道最后亮着的灯泡的数量如下图所示

将集合A表示为被a整除的数,集合B为被b整除的数,集合C为被c整除的数。

阴影部分的面积求法:S-AB-AC-BC+3ABC

而S=A+B+C-AB-AC-BC+ABC 整理得阴影部分面积为A+B+C-2*(AB+AC+BC)+4*ABC

【代码】

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,a,b,c;
        scanf("%d",&n);
        scanf("%d%d%d",&a,&b,&c);
        int sum=n/a+n/b+n/c-2*(n/a/b+n/a/c+n/b/c)+4*(n/a/b/c);//因为a,b,c都为质数所以直接相除即可,若不是质数,应该乘上最大公约数
        printf("%d\n",sum);
    }
    return 0;
}

hdu6143 Killer Names(容斥定理)

【题意】

用m个字符组合名字,名字中有姓和名分别为长度为n的字符串,姓和名中的字符不能相同,但姓中的字符可以相同,名中的字符也可以相同。

【解题思路】

用f[i]表示姓中含有i个字符

f[1]=1

f[2]=2^n-C[2][1]*f[1](这里需要运用容斥定理,减去姓中含有1个字符的情况)

f[3]=3^n-C[3][2]*f[2]-C[3][1]*f[1]

f[i]=i^n-(C[i][1]*f[1]+C[i][2]*f[2]+...C[i][i-1]*f[i-1])

姓中的字符搞定后名中的字符就简单了,最后所有方案数为 C(m,i)*f[i]*(m-i)^n  ,1<=i<=m

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=2005;
const int mod=1e9+7;
LL n,m,c[maxn][maxn],f[maxn];
void zuhe()
{
    memset(c,0,sizeof(c));
    for(int i=1;i<maxn;i++)
    {
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;j++)
        {
            c[i][j]=c[i-1][j]+c[i-1][j-1];
            c[i][j]%=mod;
        }
    }
}
LL quickpow(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}
int main()
{
    int T;
    zuhe();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&m);
        memset(f,0,sizeof(f));
        f[1]=1;
        //将递推数组打印出,运用容斥定理去重
        for(int i=2;i<=n;i++)
        {
            f[i]=quickpow(i,n);
            //printf("f[i]1=%lld\n",f[i]);
            for(int j=1;j<i;j++)
            {
                f[i]=(f[i]-c[i][j]*f[j])%mod;
                if(f[i]<0)f[i]=(f[i]+mod)%mod;
                //printf("f[i]2=%lld\n",f[i]);
            }
        }
        LL ans=0;
        //求和:C(m,i)*f[i]*(m-i)^n
        for(int i=1;i<=m;i++)
        {
            ans+=c[m][i]%mod*f[i]%mod*quickpow(m-i,n)%mod;
        }
        printf("%lld\n",ans%mod);
    }
    return 0;
}

NYOJ417 死神来了(鸽巢原理)

【题意】

在1~n个数中,随机取m个数,问在这m个数中是否一定存在一个数是另一个数的倍数,是则回答“YES",否则”NO"。

【解题思路】

考虑最小的倍数是2点情况,那么n个数中,当m≤n/2,考虑最坏的情况,n全部随机到了 n/2-n 在这个区间内所有数都是互质的。比如n=100 m=50 那么显而易见,最坏的情况选的是51-100,这些数都没有公因数,但如果 m=51的时候,最坏也会选中 50-100 50的两倍是100。同理,如果为n奇数,例如101 在 1-101之间,那么最坏情况m只有为52才能构成。

【代码】

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        int ans=n/2+(n&1);
        if(m>ans)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39826163/article/details/81506443