Interstellar Hunter【2020CCPC秦皇岛-I】

题意

在一个无限大的二维平面内,\(Alex\) 的起点为 \((0,0)\) 。他可以获得若干的数对 \((a,b)\) ,使得他可以从点 \((x,y)\) 跳跃到 \((x+a,y+b)\) 或者 \((x-a,y-b)\) ,跳跃的次数不限。同时,会给出若干的点的坐标 \((x,y)\),且该点处有 \(w\) 的能量可以获得。\(Alex\) 可以选择去获取该点的能量或者忽略它。

\(1\leq T \leq 10^4,1\leq Q \leq 10^5,0\leq x_i,y_i\leq 10^6,1\leq w_i\leq 10^9\)

题目链接:https://codeforces.com/gym/102769/problem/I

分析

对于二维平面的可以到达的点集,可以通过构造两个基向量来表示,即:\((X_1,Y_1)\)\((0,Y_2)\) ,其中,\(Y_2\geq0,X_1\geq 0,Y1<Y_2\)

假设当前加入的向量为: \((a,b)\) ,首先对于合成的新向量的 \(X\) 部分,\(X_1'=gcd(X_1,a)\) ,因为 \((0,Y_2)\) 不会对 \(X\) 产生影响。可以使用拓展欧几里得来求解,同时求出各自的系数,便于求出 \(Y_1'\)。接下来,考虑 \((X_1,Y_1)\)\((a,b)\) 进行线性组合产生一个向量 \((0,d)\) ,那么反过来,通过向量 \((X_1,Y_1)\)\((0,d)\) 的线性组合就可以产生向量 \((a,b)\) 。因此,我们只需要将 \((0,d)\)\((0,Y_2)\) 进行合并即可,显然 \(Y_2'=gcd(d,Y_2)\) 。那么,该如何合成向量 \((0,d)\) 呢?可以发现,只要使得合成后的向量的水平方向为 \(0\) 即可。那么,\(d=(X_1·b-a·Y_1)\) ,为了防止数据过大,可以除以 \(gcd(X_1,a)\)

求出了每次添加新向量之后的新基向量,对于给出的每个点,我们只需要判断是否可以用基向量表示即可。复杂度:\(O(n\log(x+y))\)

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
void read(int &x)
{
    x=0;
    int f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+ch-'0';
        ch=getchar();
    }
    x*=f;
}
ll gcd(ll x,ll y)
{
    return y?gcd(y,x%y):x;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll res=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return res;
}
int main()
{
    int T,q,cnt=0;
    read(T);
    while(T--)
    {
        read(q);
        int op,x,y,w;
        ll u=0,v=0,ans=0;
        ll g,X1=0,Y1=0,Y2=0;
        for(int i=1;i<=q;i++)
        {
            read(op);
            if(op==1)//向量的合成
            {
                read(x),read(y);
                g=exgcd(X1,x,u,v);
                if(g==0) Y2=gcd(Y2,1LL*y);
                else Y2=gcd(Y2,(x*Y1-y*X1)/g);
                if(g==0) Y1=y;
                else Y1=Y1*u+y*v;
                if(Y2) Y1=(Y1%Y2+Y2)%Y2;
                X1=g;
            }
            else
            {
                read(x),read(y),read(w);
                if(X1==0)//首先满足:x=0
                {
                    if(x!=0) continue;
                    if(Y1==0)
                    {
                        if(y!=0)
                        {
                            if(Y2!=0&&y%Y2==0)
                                ans+=w;
                        }
                        else ans+=w;
                    }
                    else
                    {
                        if(Y2==0&&y%Y1==0)
                            ans+=w;
                        else if(Y2!=0)
                        {
                            g=exgcd(Y1,Y2,u,v);
                            if(y%g==0) ans+=w;
                        }
                    }
                }
                else
                {
                    if(x%X1!=0) continue;
                    if(Y2==0)
                    {
                        if(X1*y==x*Y1)
                            ans+=w;
                        continue;
                    }
                    if((y-(x/X1)*Y1)%Y2==0)
                        ans+=w;
                }
            }
        }
        printf("Case #%d: %lld\n",++cnt,ans);
    }
    return 0;
}
posted @ 2020-10-22 17:02  xzx9  阅读(558)  评论(1编辑  收藏  举报