hdu 2841 Visible Trees&& hdu 1695 GCD(容斥定理+数论)

hdu 2841  Visible Trees

题意是给定一个n*m的网格  每一个点上都有一棵树  问从(0,0)点能够看到几棵树 

从(0,0)点看过去  如果有重叠的树  只能看到离自己最近的那一个

如果能看到点(x,y)的树  那么(k*x,k*y)  对于k>1 (k*x,k*y)上的树都不能看到

问题就可以转化为在1<=x<=n,1<=y<=m中有多少个(x,y)满足gcd(x,y)=1

在1-n枚举x  累计在1-m中有多少个y与x互素

枚举x  就可以把问题转化为在一个范围内有多少个数与y互素

然后问题就跟上一篇写的差不多了

解决这个问题时  预处理出  数据范围之内 每个数所拥有的数因子。。

size就是这个数素因子的个数  

然后用容斥原理  求个数

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 0x7fffffff

#define FOR(i,a,b)  for(int i=a;i<=b;i++)
#define FOV(i,a,b)  for(int i=a;i>=b;i--)
#define REP(i,a,b)  for(int i=a;i<b;i++)
#define REV(i,a,b)  for(int i=a-1;i>=b;i--)
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

vector<int> pri[MAXN];

void init()
{
    for(int i=0;i<MAXN;i++)
        pri[i].clear();
    for(int i=2;i<MAXN;i++)
    {
        if(pri[i].size())
            continue;//如果size不为0  说明i可以分解为两个数的乘积
            //为0说明之前的数都不能整除它
            //size表示素因子的个数
        pri[i].push_back(i);
        for(int j=2;i*j<MAXN;j++)
        {
            pri[i*j].push_back(i);
        }
    }
}

ll ans(ll order,ll m)
{
    int s=pri[order].size();
    ll res=0;
    for(ll i=1;i<(ll)(1<<s);i++)
    {
        int flag=0;
        ll tmp=1;
        for(int j=0;j<s;j++)
        {
            if(i&(ll)(1<<j))
            {
                flag++;
                tmp*=(ll)pri[order][j];
            }
        }
        if(flag&1)
            res+=m/tmp;
        else res-=m/tmp;
    }
    return res;
}


int main()
{
//freopen("ceshi.txt","r",stdin);
    init();
    int tc;
    scanf("%d",&tc);
    while(tc--)
    {
        ll m,n;
        scanf("%I64d%I64d",&m,&n);
        ll res=0;
        res+=n;
        for(ll i=2;i<=m;i++)
        {
            res+=ans(i,n);
        }
        printf("%I64d\n",res);
    }
    return 0;
}


hdu 1695 GCD

题意就是求在1<=a<=x 1<=b<=y 中满足gcd(a,b)=k的(a,b)的对数  注意  是无序的

问题转化为在[1,x/k]和[1,y/k]范围内找gcd(a,b)=1的对数 (如果gcd(a,b)=k 那么gac(a/k,b/k)=1)

因为给定5个参数  a b c d k 其中  a c 是等于1的 b/=k  d/=k  修改参数范围

令b=min(b,d)  然后用欧拉函数直接求出 1-b范围内所有数的欧拉函数的和 

这里可以减少 枚举次数 

在 b+1-d范围内的数 通过上题的 方法进行求和!!!

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 0x7fffffff

#define FOR(i,a,b)  for(int i=a;i<=b;i++)
#define FOV(i,a,b)  for(int i=a;i>=b;i--)
#define REP(i,a,b)  for(int i=a;i<b;i++)
#define REV(i,a,b)  for(int i=a-1;i>=b;i--)
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

vector<int> pri[MAXN];

void init()
{
    for(int i=0;i<MAXN;i++)
        pri[i].clear();
    for(int i=2;i<MAXN;i++)
    {
        if(pri[i].size())
            continue;//如果size不为0  说明i可以分解为两个数的乘积
            //为0说明之前的数都不能整除它
            //size表示素因子的个数
        pri[i].push_back(i);
        for(int j=2;i*j<MAXN;j++)
        {
            pri[i*j].push_back(i);
        }
    }
}

ll ans(ll order,ll m)
{
    int s=pri[order].size();
    ll res=0;
    for(ll i=1;i<(ll)(1<<s);i++)
    {
        int flag=0;
        ll tmp=1;
        for(int j=0;j<s;j++)
        {
            if(i&(ll)(1<<j))
            {
                flag++;
                tmp*=(ll)pri[order][j];
            }
        }
        if(flag&1)
            res+=m/tmp;
        else res-=m/tmp;
    }
    return m-res;
}

int phi(int n)
{
    int res=n;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            res=res-res/i;
            while(n%i==0)
            {
                n/=i;
            }
        }
    }
    if(n>1)
        res=res-res/n;
    return res;
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    init();
    int tc;
    scanf("%d",&tc);
    int cs=1;
    while(tc--)
    {
        ll a,b,c,d,k;
        ll res=0;
        scanf("%I64d%I64d%I64d%I64d%I64d",&a,&b,&c,&d,&k);
        printf("Case %d: ",cs++);
        if(k==0)
        {
            printf("0\n");
            continue;
        }
        if(d<b)//让b取小值
            swap(b,d);
        for(int i=1;i<=b/k;i++)
            res+=phi(i);
        for(ll i=b/k+1;i<=d/k;i++)
        {
//            cout<<"ans  "<<ans(i,b)<<endl;
            res+=ans(i,b/k);
        }
        printf("%I64d\n",res);
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值