1. 程式人生 > 實用技巧 >【POJ 3090】dp遞推求邊長為n的方格,有多少個點能被(0,0)點看見

【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]);
	}
}