UVA - 1393 Highways(二维前缀和+dp)

传送门


首先一个显而易见的GCD性质是当且仅当 g c d ( a b s ( x 1 x 2 ) , a b s ( y 1 y 2 ) ) = 1 gcd(abs(x1-x2),abs(y1-y2))=1 时两点连线不经过其他点。我们设数组 d [ i ] [ j ] d[i][j] 表示 ( 1 , 1 ) (1,1) ( i , j ) (i,j) 之间的合法直线个数,如下图所示:
在这里插入图片描述

根据二维前缀和的知识,得到:

d [ i ] [ j ] = d [ i 1 ] [ j ] + d [ i ] [ j 1 ] d [ i 1 ] [ j 1 ] + ( g c d [ i ] [ j ] = = 1 ) d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(gcd[i][j]==1)

实际上我们计算答案时需要的是 ( i , j ) (i,j) 到其他点的合法直线个数,这并不难,对称过去实际上和上述结果一样的

但是这样并不是最后的答案,使用二维前缀和优化dp,那么答案显然需要预处理出的前缀和来计算,假设当前点是 ( i , j ) (i,j) ,只需要知道三个点的 d d 值便可求出所有的情况:

即之前的所有情况 d [ i , j 1 ] + d [ i 1 , j ] d [ i 1 ] [ j 1 ] d[i,j-1]+d[i-1,j]-d[i-1][j-1] 再加上现在的所有情况 d [ i ] [ j ] d[i][j] ,但是这样显然有一些直线是重复的,需要减去 d [ i / 2 , j / 2 ] d[i/2,j/2] ,为什么?因为重复的直线肯定是 g c d ( a b s ( x 1 x 2 ) , a b s ( y 1 y 2 ) ) > 1 gcd(abs(x1-x2),abs(y1-y2))>1 ,因为大于1的最小因数是 2 2 ,因此只需除以2便可计算出所有的重复直线,以 ( 4 , 4 ) (4,4) 为例,如下图所示:
在这里插入图片描述
那么最后的答案就是:

a n s [ i ] [ j ] = d [ i , j 1 ] + d [ i 1 , j ] d [ i 1 ] [ j 1 ] + d [ i ] [ j ] d [ i / 2 ] [ j / 2 ] ans[i][j]=d[i,j-1]+d[i-1,j]-d[i-1][j-1]+d[i][j]-d[i/2][j/2]

ps:题目给的 n , m n,m 是点的个数而不是线段长度,因此答案为 a n s [ n 1 ] [ m 1 ] ans[n-1][m-1]

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<double,double> pdd;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const double dinf=1e300;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=305;

int gcd[maxn][maxn];
ll d[maxn][maxn],ans[maxn][maxn];

void init(){
    for(int i=1;i<maxn;i++)
        for(int j=1;j<maxn;j++) if(!gcd[i][j])
            for(int k=1;i*k<maxn && j*k<maxn;k++)
                gcd[i*k][j*k]=k;

    for(int i=1;i<maxn;i++)
        for(int j=1;j<maxn;j++)
            d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(gcd[i][j]==1?1:0);

    /*for(int i=1;i<maxn;i++){
        for(int j=1;j<maxn;j++)
            cout<<d[i][j]<<" ";
        cout<<endl;
    }*/
    for(int i=1;i<maxn;i++)
        for(int j=1;j<maxn;j++)
            ans[i][j]=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-1]+d[i][j]-d[i/2][j/2];

}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    init();
    int n,m;
    while(cin>>n>>m && n){
        cout<<ans[n-1][m-1]*2<<"\n";
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/107676678