HDU 2841 Visible Trees(数论)

题目大意:给你一个m*n的方格,方格从(1,1)开始。每个点上有一棵树,然后让你从(0,0)点向下看,问你能看见几颗树。

解题思路:如果你的视线被后面的树和挡住的话以后在这条线上的树你是都看不见的啊。挡住的话就是这个小的方格内对角线的连线过顶点,如图:


建设A为(0,0)如果B能遮挡住C,那么B与C组成的矩形中nx, mx是不互质的。

所以x从1开始枚举n如果m中某一项与x互质那么就会有一个格子显示出来,所以这里需要找的是1-n中与枚举的x互质的个数。这里的话我们可以用到分解质因数,然后用状态压缩进行容斥,求出n中与x互质的个数。

PS:我代码里面写的n,m与这里解释的n,m是反过来的。


Visible Trees

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1556    Accepted Submission(s): 645


Problem Description
There are many trees forming a m * n grid, the grid starts from (1,1). Farmer Sherlock is standing at (0,0) point. He wonders how many trees he can see.

If two trees and Sherlock are in one line, Farmer Sherlock can only see the tree nearest to him.
 

Input
The first line contains one integer t, represents the number of test cases. Then there are multiple test cases. For each test case there is one line containing two integers m and n(1 ≤ m, n ≤ 100000)
 

Output
For each test case output one line represents the number of trees Farmer Sherlock can see.
 

Sample Input
  
  
2 1 1 2 3
 

Sample Output
  
  
1 5
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define eps 1e-8
#define M 1000100
#define LL long long
//#define LL long long
#define INF 0x3f3f3f
#define PI 3.1415926535898
#define mod 1000000007


const int maxn = 100010;

using namespace std;

struct node
{
    int num[8];
    int ans;
} p[maxn];

int f[maxn];
int k[maxn];
int t;
void prime()
{
    t = 0;
    memset(f, 0, sizeof(f));
    for(int i = 2; i <= maxn-5; i++)
    {
        if(!f[i]) k[t++] = i;
        for(int j = 0; j < t; j++)
        {
            if(i*k[j] > maxn-5) break;
            f[i*k[j]] = true;
            if(i%k[j] == 0) break;
        }
    }
}


LL judge(int n, int x)
{
    LL cnt = 0;
    for(int i = 0; i < (1<<(p[x].ans)); i++)
    {
        int ss = 0;
        int sx = 1;
        for(int j = 0; j < p[x].ans; j++)
        {
            if((1<<j)&i)
            {
                ss++;
                sx *= p[x].num[j];
            }
        }
        if(ss%2) cnt += (n-n/sx);
        else cnt -= (n-n/sx);
    }
    return cnt;
}

int main()
{
    prime();
    for(int i = 2; i <= maxn-10; i++)
    {
        int x = i;
        p[i].ans = 0;
        for(int j = 0; j < t; j++)
        {
            if(x == 1) break;
            if(!f[x])
            {
                p[i].num[p[i].ans++] = x;
                break;
            }
            if(x%k[j]) continue;
            p[i].num[p[i].ans++] = k[j];
            while(x%k[j] == 0) x /= k[j];
        }
    }
    int n, m;
    int T;
    cin >>T;
    while(T--)
    {
        scanf("%d %d",&n, &m);
        LL sum = n;
        for(int i = 2; i <= m; i++) sum += judge(n, i);
        cout<<sum<<endl;
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值