【POJ 3090】dp递推求边长为n的方格,有多少个点能被(0,0)点看见

【POJ 3090】dp递推求边长为n的方格,有多少个点能被(0,0)点看见

题意

一道递推dp题目,求一个\(n * n\)的方格,有多少格点能被(0,0)点看见

能看见是指(0,0)点到该点连线上没有其他格点

\(dp[i]\)表示\(i*i\)方格有\(dp[i]\)个格点能被(0,0)看见,

那么考虑如何转移到从dp[i-1]转移过来

一个方格升一阶,相当于在右侧和上侧加了一列一行

显然的是新增的一行一列对原来已有的答案不产生影响

那么只需要考虑新增的格点有哪些能对答案产生贡献

通过简单的观察可得,可见格点的分布是以y=x为对称轴对称的,所以只需要考虑一侧即可

这里我们考虑最上面增加的一行

通过简单观察可知,假设最上面一行纵坐标为y,横坐标范围为1~n

横坐标x与y最大公因数>1该格点不能被看见,因为只需要横纵坐标同时约掉最大公因数后得到的结点就是那个挡住当前结点的点,因此当前结点不能被看见。

所以转移方程:\(dp[i]=dp[i-1]+sum(1到i的数中与y互质的数的个数)*2\)

/****************************
* Author : W.A.R            *
* Date : 2020-11-01-20:36   *
****************************/
/*
*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<set>
#define IOS ios::sync_with_stdio(false)
#define show(x) std:: cerr << #x << " = " << x << std::endl;
#define mem(a,x) memset(a,x,sizeof(a))
#define Rint register int
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int maxm=2e6+10;
const ll mod=1e9+7;
const double PI=acos(-1.0);
const double eps=1e-7;
int dp[1050];
int main(){
	dp[1]=3;
	for(int i=2;i<=1000;i++){
		int sum=0;
		for(int j=2;j<i;j++){
			if(__gcd(i,j)!=1)sum++;
		}
		sum=i-1-sum;
		dp[i]=dp[i-1]+sum*2;
	}
	int T;scanf("%d",&T);
	for(int i=1;i<=T;i++){
		int n;
		scanf("%d",&n);
		printf("%d %d %d\n",i,n,dp[n]);
	}
}
posted @ 2020-11-01 21:01  AnranWu  阅读(75)  评论(0编辑  收藏  举报