description
长跑的目的不是更快,而是更强。 ——zjp’s blog
zjp最近迷上了长跑。为了防止被zjp强锋吹拂,小狗们决定躲到狗窝里去,现在已知有n条狗在一个二维平面直角坐标系的第一象限内。
狗是一种特殊的生物,每只在(x, y)的狗走一步只能到达(x + y, y),(x, y +x),(x − y, y),(x, y − x)这四个位置中的任意一个。并且任何时候,狗都不能在坐标轴上或在到达其它象限内的位置。
每个狗窝只能容纳一条狗,我们知道n个狗窝的坐标(也在第一象限内),每条狗不一定要到其对应编号的狗窝。
经过狗精密的计算发现,当所有狗到达狗窝的步数和最小时,狗是最安全的,尽管有的狗可能要走较多的步数。
现在,你只需要告诉他们:所有狗都到达狗窝的最小步数和。
analysis
-
虚树+++哈希,这题有点神仙……但不难打
-
先定义平面上的点向外移动指横或纵坐标变大,向里移动指横或纵坐标变小,而且容易知道移动可逆
-
思考一下,(x,y)(x,y)(x,y)可以向外移动到(x+y,y),(x,y+x)(x+y,y),(x,y+x)(x+y,y),(x,y+x),但只能向里移动到两种中的一个(因为x−y≤0x-y≤0x−y≤0或y−x≤0y-x≤0y−x≤0)
-
那么任意一个点都是向外扩展出两个点且向里扩展出一个点,把它们全部连起来,其实就是二叉树
-
把所有点的横纵坐标除掉所有横纵坐标的gcdgcdgcd,此时所有的点都可从(1,1)(1,1)(1,1)移动得到(数据保证合法)
-
因为移动是可逆的,所以狗窝可以视作和狗一样可以在二叉树上移动,那么把狗窝也算可移动的点
-
观察(x−y,y),(x,y−x)(x-y,y),(x,y-x)(x−y,y),(x,y−x)并手动模拟点向里移动的过程,发现其实就是(x,y)(x,y)(x,y)两数求gcdgcdgcd的过程
-
设x>yx>yx>y,具体就是,xxx变成xmodyxmodyxmody,yyy再变成ymodxymodxymodx,这样下去
-
这样变化,一定是xxx先比yyy大,再yyy比xxx大下去,在二叉树上的变化就是扭来扭去,这个会变化log\loglog次
-
我们用哈希只把每个点和中间的转折点给建出来,边权即为x+yx+yx+y,这样建出一棵虚树,根为(1,1)(1,1)(1,1)
-
把狗权值视作111,狗窝权值视作−1-1−1,那么类似树上差分,让111和−1-1−1配对,同一子树里尽量配对最优
-
这个一步步向上配对就可以,回溯时记录该子树内还有几个狗或窝没配对即可
code
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define mod 3000007
#define MAXN 3000005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define rep(i,a,b) for (reg i=last[a][b];i;i=next[i][b])
#define O3 __attribute__((optimize("-O3")))
using namespace std;
vector<ll>f[MAXN][2];
ll tr[MAXN],depth[MAXN];
ll n,m,bz,tot,ans,total;
struct node
{
ll x,y;
}a[MAXN];
struct point
{
ll x,y;
}hash[mod+5];
O3 inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
O3 inline ll add(ll x,ll y)
{
ll now=((x%mod)*258280327+(y%mod)+1000000007)%mod;
while (hash[now].x && (hash[now].x!=x || hash[now].y!=y))(now+=1)%=mod;
if (hash[now].x==0)hash[now].x=x,hash[now].y=y,bz=0,depth[now]=x+y;
return now;
}
O3 inline void link_tree(ll x,ll y)
{
bz=1;
ll now=add(x,y),las,temp;
if (bz)return;
while (x!=y)
{
las=add(x,y);
if (x>y)x%=y,temp=0;
else y%=x,temp=1;
if (x==0 || y==0)x=y=1;
bz=1,now=add(x,y);
f[now][temp].push_back(las);
if (bz)return;
}
}
O3 inline ll gcd(ll x,ll y)
{
return !(x%y)?y:gcd(y,x%y);
}
O3 inline bool cmp(ll x,ll y)
{
return depth[x]>depth[y];
}
O3 inline void dfs(ll x)
{
ll cnt=0;
fo(j,0,1)
{
if (!f[x][j].size())continue;
fo(i,0,f[x][j].size()-1)dfs(f[x][j][i]);
sort(f[x][j].begin(),f[x][j].end(),cmp);
ll las=f[x][j][0],val=tr[las],bz=j?hash[x].x:hash[x].y;
fo(i,1,f[x][j].size()-1)
{
ans+=abs(val)*((depth[las]-depth[f[x][j][i]])/bz);
val+=tr[f[x][j][i]],las=f[x][j][i];
}
cnt+=val,ans+=abs(val)*((depth[las]-depth[x])/bz);
}
tr[x]+=cnt;
}
O3 int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n=read();
fo(i,1,n<<1)tot=gcd(tot,gcd(a[i].x=read(),a[i].y=read()));
fo(i,1,n<<1)a[i].x/=tot,a[i].y/=tot,link_tree(a[i].x,a[i].y);
fo(i,1,n)++tr[add(a[i].x,a[i].y)];
fo(i,n+1,n<<1)--tr[add(a[i].x,a[i].y)];
dfs(add(1,1));
printf("%lld\n",ans);
return 0;
}