HDU2841 Visible Trees(容斥原理)
题目。。大概就是有个m*n个点的矩形从(1,1)到(m,n),问从(0,0)出发直线看过去最多能看到几个点。
如果(0,0)->(x,y)和(0,0)->(x',y')两个向量平行,那后面的那个点就看不到了。
因此给出一个点(x,y),判断它能否被看到,就是是否能找到一个大于1的k,使k|x且k|y。
这样,问题就能转变为有几个点的x、y找不到公约数,即有几对x、y,满足x和y互质。
可以通过枚举x,看有几个y与其互质累加。这样问题就又变成,区间有几个数与某个数互质,经典的容斥问题HDU4135。
时间复杂度方面,1 ≤ m, n ≤ 100000,前7个素数乘积就超过100000了,即一个数最多有6个质因数,大概估个非常松的上界$O((\sqrt m+2^6)n)$,应该是妥妥的。
#include<cstdio> #include<cstring> using namespace std; int prime[7],pn; int getCnt(int n,int m){ pn=0; for(int i=2; i*i<=m; ++i){ if(m%i) continue; while(m%i==0) m/=i; prime[pn++]=i; } if(m!=1) prime[pn++]=m; int res=0; for(int i=1; i<(1<<pn); ++i){ int tmp=1,cnt=0; for(int j=0; j<pn; ++j){ if(((i>>j)&1)==0) continue; tmp*=prime[j]; ++cnt; } if(cnt&1) res+=n/tmp; else res-=n/tmp; } return n-res; } int main(){ int t,n,m; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); long long res=0; for(int i=1; i<=n; ++i){ res+=getCnt(m,i); } printf("%lld\n",res); } return 0; }