jzoj 6011.【NOIP2019模拟1.25A组】天天爱跑步 虚树

5 篇文章 0 订阅

Description
长跑的目的不是更快,而是更强。 ——zjp’s blog
zjp最近迷上了长跑。为了防止被zjp强锋吹拂,小狗们决定躲到狗窝里去,现在已知有n条狗在一个二维平面直角坐标系的第一象限内。
狗是一种特殊的生物,每只在(x, y)的狗走一步只能到达(x + y, y),(x, y +x),(x − y, y),(x, y − x)这四个位置中的任意一个。并且任何时候,狗都不能在坐标轴上或在到达其它象限内的位置。
每个狗窝只能容纳一条狗,我们知道n个狗窝的坐标(也在第一象限内),每条狗不一定要到其对应编号的狗窝。
经过狗精密的计算发现,当所有狗到达狗窝的步数和最小时,狗是最安全的,尽管有的狗可能要走较多的步数。
现在,你只需要告诉他们:所有狗都到达狗窝的最小步数和。

Input
从文件a.in中读入数据.
第一行,包含一个正整数n,表示狗以及狗窝数。
接下来n行,每行包含两个正整数,表示每只狗的最初位置。
接下来n行,每行包含两个正整数,表示每个狗窝的位置。

Output
输出到文件a.out中.
仅包含一行,一个整数,表示所有狗都到达狗窝的最小步数和。

Sample Input1
1
203 235
481 171

Sample Input2
2
1 2
4 7
3 2
7 3

Sample Output1
6

Sample Output2
3

Data Constraint
对于所有数据,有 n ≤ 5 × 1 0 4 n ≤ 5 × 10^4 n5×104,坐标范围 ≤ 1 0 18 ≤10^{18} 1018,保证任意一对狗和狗窝可达。
设最远的一对狗和狗窝相距m步。
• 对于10%的数据: n = 1 , 1 ≤ m ≤ 14 n = 1,1 ≤ m ≤ 14 n=11m14
• 对于30%的数据: n = 1 , 1 ≤ m ≤ 500 n = 1,1 ≤ m ≤ 500 n=11m500
• 对于50%的数据: n ≤ 200 , 1 ≤ m ≤ 500 n ≤ 200,1 ≤ m ≤ 500 n2001m500
• 对于70%的数据: n ≤ 1 0 4 , 1 ≤ m ≤ 500 n ≤ 10^4,1 ≤ m ≤ 500 n1041m500
• 对于最后30%的数据,没有特殊的约定

分析:
显然一个点只有 3 3 3中变化。
我们设 x > y x>y x>y,这三种变化分别为 ( x + y , y ) (x+y,y) (x+y,y) ( x , x + y ) (x,x+y) (x,x+y) ( x − y , y ) (x-y,y) (xy,y)
我们把加的看作是这个点的两个儿子,减得看做父亲,发现这是一棵二叉树,根节点的坐标为 ( g c d ( x , y ) , g c d ( x , y ) ) (gcd(x,y),gcd(x,y)) (gcd(x,y),gcd(x,y))
70分就直接把路径上的点标出来,然后把所有点连成一棵树,然后树上统计即可,计算答案是比较简单的。
100分就是要保留一些关键点,这些关键点就是直接对 ( x , y ) (x,y) (x,y)进行辗转相除的那些点。
当我们发现一对 ( x ′ , y ′ ) (x',y') (x,y)已经出现过,就把这个信息挂在这个点上,注意分类,然后从小到大连上即可。
我的程序直接暴力动态修改,就是暴力跳儿子直到权值介于父亲和儿子之间,这样强行卡过了。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#define LL long long

const int maxn=2e6+7;

using namespace std;

int n,m,root,cnt;
int sum[maxn];
LL ans,x,y;

struct rec{
	LL x,y;
};

bool operator <(rec a,rec b)
{
	if (a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}

struct node{
	int l,r,op;
	LL x,y;
	LL len1,len2;
}t[maxn];

map <rec,int> num;

int getabs(int x)
{
	if (x>0) return x;
	return -x;
}

void updata(int x)
{
	if (t[x].l) t[x].len1=(t[t[x].l].x-t[x].x)/t[t[x].l].y;
	if (t[x].r) t[x].len2=(t[t[x].r].y-t[x].y)/t[t[x].r].x;
}

void build(LL S,LL T,int d)
{
	int e=num[(rec){S,T}];
	if (e)
	{
		t[e].op+=d;
		return;
	}
	LL x=S,y=T,lastx=0,lasty=0;
	int op;
	while (!num[(rec){x,y}])
	{
		num[(rec){x,y}]=++cnt;
		t[cnt].x=x,t[cnt].y=y;
		if (lastx)
		{
			if (op==0) t[cnt].l=cnt-1;
			      else t[cnt].r=cnt-1;
			updata(cnt);
		}
		if (x==y) break;
		lastx=x,lasty=y;
		if (x>y) x%=y,op=0;
		    else y%=x,op=1;
		if (!x) x=y;
		if (!y) y=x;
	}
	t[num[(rec){S,T}]].op+=d;
	int c=num[(rec){x,y}];
	while (true)
	{
		int last=c;
		if ((op==0) && (t[c].l) && (t[t[c].l].x<lastx)) c=t[c].l;
		if ((op==1) && (t[c].r) && (t[t[c].r].y<lasty)) c=t[c].r;
		if (c==last) break;
	}
	if (op==0)
	{
		int p=c,q=t[p].l;
		t[p].l=cnt;
		if (q) t[cnt].l=q;
		updata(cnt),updata(p);
	}
	else
	{
		int p=c,q=t[p].r;
		t[p].r=cnt;
		if (q) t[cnt].r=q;
		updata(cnt),updata(p);
	}
}

void dfs(int x)
{
	if (t[x].l)
	{
		dfs(t[x].l);
		ans+=(LL)abs(sum[t[x].l])*t[x].len1;
		sum[x]+=sum[t[x].l];
	}
	if (t[x].r) 
	{
		dfs(t[x].r);
		ans+=(LL)abs(sum[t[x].r])*t[x].len2;
		sum[x]+=sum[t[x].r];
	}
	sum[x]+=t[x].op;
} 

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d",&n);		
	for (int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&x,&y);
		build(x,y,1);
		if (!root) root=cnt;
	}	
	for (int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&x,&y);
		build(x,y,-1);
	}		
	dfs(root);
	printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值