Visible Trees
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1356 Accepted Submission(s): 560
If two trees and Sherlock are in one line, Farmer Sherlock can only see the tree nearest to him.
2 1 1 2 3
1 5
题意:给n*m的矩阵有点,左下角的点为(1,1),右上角的点(n,m),(其实转回来也是没影响的即m*n),一个人站在(0,0)看这些点,在一条直线的视线上,它只能看到最前面的那个点,后面的点将会被档住他看不到,问你,这个人一共能看到多少个点。
这个问题只要画一下图不难发现,如果一个点(x,y),x和y有非1的公约数z,那么他们其实可以一起缩小为(x/z,y/z),试着把这两个点和(0,0)连线,发现他们其实是同一条直线,而(x/z,y/z)
在前面,所以其实(x,y)被挡住了看不到的,这启发了我们,如果我们找到了x和y的最大公约数g,那么(x/g,y/g)一定是这条直线上最前面的点,没有其他店能挡住他,他一定能被看到,而他后面的点都看不到,那么(x/g,y/g)满足的性质就是,这两个数字互质
从而得到一个结论,两个数字(x,y)如果两数互质,则可以被看到,如果不互质,则看不到,所以我们就是要找出所有的二元组(x,y)使他们互质
我们可以固定一个数字,用一个数来循环。例如矩阵为n*m,我们固定m,用n来循环,即1与[1,m]里面多少个数互质,2与[1,m]里面多少个数互质,3与[1,m]里面多少个数互质……n与[1,m]里面多少个数互质,把这些结果全部累加起来即可
所以问题的最后变为了,给定一个数字x,怎么找出它和1到y里面有多少个数互质呢?
两个数字互质,其实就是它们没有公共的质因子,反过来两个数字有公共的质因子则一定不互质,那么我们可以求反面,x与1到y里面多少个数字不互质,然后用y减去即可
在这里我们就用到了容斥原理:先找到有多少个数和x有1个公共的质因子,然后加上;再找到有多少个数与x有2个公共的质因子,然后减去;再找到有多少个数有多少个数与x有3个公共的质因子,然后加上……最后得到的个数,就是有多少个数与x不互质
因为容斥原理一个最基本的准则就是——
要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。(奇数加,偶数减)
说到这里,再看代码已经不是问题
两种求1~n与m互质的数的个数算法,cal2速度较快一点20ms左右=。=#define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 0x3f3f3f3f
#define EPS 1e-6
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
#define N 100005
bool p[N];
vector<int>fac[N];//保存每个数字带有的质数因子
void init()
{
for (int i = 0; i < N; i++)
fac[i].clear();
memset(p, 0, sizeof(p));
for (int i = 2; i < N; i++)
{
if (!p[i])
{
fac[i].push_back(i);
for (int j = i + i; j < N; j += i)
{
p[j] = true;//不是质数因子
fac[j].push_back(i);//j这个数字含有质因子i
}
}
}
}
//算法1,集合法
int cal(int n, int m)
{
int size = (int)fac[n].size();
//得到n这个数字有多少个质数因子
int maxs = 1 << size;//每个选或不选生成的子集个数
int Count = 0;//记录n与1-m这m个数中有多少个互质
for (int s = 1; s < maxs; s++)
//美剧自己,不能有空集所以从1开始
{
int k = 0, num = 0, pro = 1;
for (int i = 0; i < size; i++)
{
if (s & (1 << i)) //含有因子i
{
num++;//计数
pro *= fac[n][i];
//乘上这个质因子
}
}
//奇加偶减——容斥原理
if (num & 1)//
Count += m / pro;
else
Count -= m / pro;
}
return m - Count;
}
int que[10000];//保存分母
int cal2(int n, int t)
{
int num = 0;
que[num++] = 1;//开始为1,即t-(t/x+t/x-t/xx-t/xx+t/xxx)
for (int i = 0; i < fac[n].size(); i++)
{
int ep = fac[n][i];
int k = num;
for (int j = 0; j < k; j++)
que[num++] = ep * que[j] * (-1);//对应奇加偶减
}
int sum = 0;
for (int i = 0; i < num; i++)
sum += t / que[i];
return sum;
}
int main()
{
#ifdef DeBUGs
freopen("C:\\Users\\Sky\\Desktop\\1.in", "r", stdin);
#endif
int T, H, W;
init();
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &H, &W);
if (W > H)
{
swap(W, H);
}
long long ans = H;
for (int w = 2; w <= W; w++)
ans += cal2(w, H); //1-W 与H互质个数
printf("%I64d\n", ans);
}
return 0;
}