<8/1>集训日记

    今天看了有关容斥原理的几篇博客。

    容斥原理的基本思想是:先不考虑重叠的情况,把所有对象的数目求出,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复。

    首先是一个经典的互素问题[HDOJ 4135 Co-prime]

    题目大意是:给定A,B,N,求区间[A,B]中与N互素的数的个数。

    思路是:先将N的所有素因子求出,然后求区间[1,A]中与N具有公因子的数的个数,然后用[1,B]减去[1,A-1]。

    代码如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<queue>//队列
#include<stack>//栈
#include<algorithm>
#include<iostream>
using namespace std;
long long p[100],k;
void getp(long long n)//求出n的所有素因子 放入p[i]
{
    long long i;
    k=0;
    for(i=2;i*i<=n;i++)  
    {  
        if(n%i==0)  
        p[k++]=i;  
        while(n%i==0)  
        n/=i;  
    }  
    if(n>1)  
    p[k++]=n;  
}  
long long nop(long long m)  //nop(m)得到的是区间[1,m]中所有与n具有公因子的数的个数
{  
    long long que[100000],i,j,sum,top,t;  
    que[0]=-1;//另que首项为1,可保证各项乘积的正负性,即容斥原理的思想

    top=1;  
    for(i=0;i<k;i++)  //这个循环实现了:向que[i]中放入了各个因子的乘积(包含与1的乘积)(带正负号)
    {  
        t=top;  
        for(j=0;j<t;j++)  
        {  
            que[top++]=que[j]*p[i]*(-1);  //这个是关键
        }  
    }  
    for(i=1,sum=0;i<top;i++)  
    sum+=m/que[i];  //求和,得到个数
    return sum;  
}  
int main()  
{  
    long long a,b,n;  
    int t,j=1;  
    scanf("%d",&t);  
    while(t--)  
    {  
        scanf("%lld%lld%lld",&a,&b,&n);  
        getp(n);  //求出n的所有素因子
        printf("Case #%d: %lld\n",j++,b-nop(b)-(a-1-nop(a-1)));  // b-nop(b)为区间[1,b]中与n互素的数的个数,所以这个输出的式子是用[1,b]减去[1,a-1]
    }  
    return 0;  

 

然后是一个比较巧的题,Visible Trees,给一个[m,n]的矩阵(从[1,1]开始),每个点上有一颗树,站在[0,0]点看矩阵,前面的树会挡着后面的树,问此时一共可以看到多少树。

将这个题转化为数学问题,被挡住的树的坐标一定是前面某颗树坐标的倍数,也就是任意一点(a,b),如果a,b有约数,那么它一定会被挡住。所以问题转化为了求互质点对数,即用容斥原理可解决,与上题代码相似。

容斥原理的几个题大概都是这样一个思路,弄清各项间的加减关系即可。


下午尝试了一下模拟赛中的第三题,大意是,在某一次游戏中,两个人基础分为1,先选定一个任意数K(不重要),然后每一局获胜的人将分数乘K的平方,输的人将分数乘K,游戏进行N局后得到两人的成绩。问题是输入了一组成绩,问其正确性,是否是某次游戏的成绩。

题意理解后,可以得出结论:两个人的成绩的乘积一定是某个数的三次方,且两个人的成绩必须是该数的倍数。

我的代码如下:

#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<cmath>
#include <algorithm>
using namespace std;
int main(){
    long long int a,b,c,e,f;
    int t;
    scanf("%d", &t);//出现了超时情况,同学提醒我更改了输入输出方式
    while(t--)
    {
        c=0;
        scanf("%I64d %I64d", &a, &b);
        c=a*b;
        if(a==1&&b==1) puts("Yes");
        else if(a==1||b==1) puts("No");
        else{
        e=(int)(pow(c,1.0/3)+0.5);//pow(a,n)求a的n次方
        f=(long long)e*e*e;
        if((f==c)&&(a%e==0)&&(b%e==0)) puts("Yes");
        else puts("No");}
    }
}

以上~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值