HDU-2841-Visible Trees
这道题是容斥哟~这个地方我用的是队列数组(其实容斥实现的地方dfs或者位运算均可)
先开始是在容斥的知识点看到写的=-=后来卡了许久,又换到莫比乌斯反演上面去看,最后还是回到容斥上面去了。
题目大意:农夫夏洛克站在坐标为(0,0)的位置,他去看一个每个网格点都种了树的m*n的网格。问可以看到多少棵树,其中如果有两颗以上的树与夏洛克在同一条直线上,那么夏洛克只能看到一棵树。
本题思路:这道题转换过来其实就是让我们求在区间[1, m]与[1, n]这些数里面(i, j)其中i与j互质的对数有多少。
根据题目意思:在与夏洛克在同一条直线上面的树只能看到一棵,也就是斜率相等的看不见。所以转换过来就是让我们求互质的数有多少对了。
这道题的思路跟HDU 4135差不多,代码其实也差不多,可以直接参考HDU 4135的博客,然后这道题的思路我就不多说啦:
https://blog.csdn.net/qq_44624316/article/details/105972702
我们首先用埃筛来线性得到质数。a[]数组:a[i]得到的是i的质因数。
埃筛的博客参考:https://blog.csdn.net/qq_44624316/article/details/105431013
然后我们再把[2, n]跑一遍,直接用容斥来计算互质的对数有多少,维护答案ans就行~,记得使用ll。
ans初始化为m,m代表行数,我们在循环遍历的时候是一列一列的计算的,第一列的树是我们全部可以看到的,所以就是上面初始化的原因。
q[]数组就是我们的队列数组啦~
cal()函数就是计算每列哪些树可以看到的,返回的是可以看到的树的数目,(就是互质的对数)
代码部分:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;
int m, n;
ll ans;
vector<int> a[N];
int q[N];
void init()
{
for (int i = 0; i < N; i++)
{
a[i].clear();
}
for (int i = 2; i < N; i++)
{
int t = i;
for (int j = 2; j * j <= i; j++)
{
if (!(t % j))
{
a[i].push_back(j);
while (!(t % j))
{
t /= j;
}
}
}
if (t > 1)
{
a[i].push_back(t);
}
}
}
ll cal(int x, int s)
{
int num = 0;
q[num++] = 1;
int siz = a[s].size();
for (int i = 0; i < siz; i++)
{
int t = a[s][i];
if (t > x)
{
break;
}
int k = num;
for (int j = 0; j < k; j++)
{
q[num++] = q[j] * t * (-1);
}
}
ll sum = 0;
for (int i = 0; i < num; i++)
{
sum += x / q[i];
}
return sum;
}
int main()
{
init();
int t;
cin >> t;
while (t--)
{
cin >> m >> n;
ans = m;
for (int i = 2; i <= n; i++)
{
ans += cal(m, i);
}
cout << ans << endl;
}
return 0;
}