HDU-7311 2023“钉耙编程”杭电多校赛(3)Noblesse Code

该文详细解释了一道编程竞赛题目,涉及到n个数对和q次查询。每次查询(A,B)可以通过特定操作变换,目标是确定(A,B)可以变换为给定n个数对中的多少个。利用辗转相除法和二叉树模型,可以求解每个查询的答案,时间复杂度为O((n+q)log(n+q)logmx)。
摘要由CSDN通过智能技术生成

HDU-7311 2023“钉耙编程”杭电多校赛(3)Noblesse Code

题目大意

n n n个数对 ( a 1 , b 1 ) , ( a 2 , b 2 ) , … , ( a n , b n ) (a_1,b_1),(a_2,b_2),\dots,(a_n,b_n) (a1,b1),(a2,b2),,(an,bn),以及 q q q次查询。

每次查询有一个数对 ( A , B ) (A,B) (A,B),你每次可以将当前的 ( A , B ) (A,B) (A,B)变换成 ( A + B , B ) (A+B,B) (A+B,B) ( A , A + B ) (A,A+B) (A,A+B),你可以执行这个操作任意次,求 ( A , B ) (A,B) (A,B)可以变换为上面 n n n个数对中的多少个。

T T T组数据。

1 ≤ T ≤ 100 , 1 ≤ n , q ≤ 5 × 1 0 4 , 1 ≤ a i , b i , A , B ≤ 1 0 18 1\leq T\leq 100,1\leq n,q\leq 5\times 10^4,1\leq a_i,b_i,A,B\leq 10^{18} 1T100,1n,q5×104,1ai,bi,A,B1018

数据保证 ∑ n ≤ 5 × 1 0 5 , ∑ q ≤ 5 × 1 0 5 \sum n\leq 5\times 10^5,\sum q\leq 5\times 10^5 n5×105,q5×105


题解

对于一个二元组 ( A , B ) (A,B) (A,B),它的前驱二元组可以唯一确定:

  • A = B A=B A=B时, ( A , B ) (A,B) (A,B)没有前驱二元组
  • A < B A<B A<B时, ( A , B ) (A,B) (A,B)的前驱二元组为 ( A , B − A ) (A,B-A) (A,BA)
  • A > B A>B A>B时, ( A , B ) (A,B) (A,B)的前驱二元组为 ( A − B , B ) (A-B,B) (AB,B)

如果将其看做二叉树,那么前驱二元组可以看作当前二元组的父亲, A < B A<B A<B的情况为左儿子, A > B A>B A>B的情况为右儿子。那么,根据辗转相除法,一个二元组 ( A , B ) (A,B) (A,B)所在的树的根节点为 ( gcd ⁡ ( A , B ) , gcd ⁡ ( A , B ) ) (\gcd(A,B),\gcd(A,B)) (gcd(A,B),gcd(A,B))

那么,对于每次询问 ( A , B ) (A,B) (A,B),即求 ( A , B ) (A,B) (A,B)对应的点在树上向下走可以到达的表示这 n n n个数对的点的数量。

对于每个二元组,找出从根到它的路径,用 1 1 1表示向左走,用 2 2 2表示向右走,以此将这条路径存入一个 vector \text{vector} vector中,还要在 vector \text{vector} vector的开头加上数字 gcd ⁡ ( A , B ) \gcd(A,B) gcd(A,B)以区分不同的根节点。

那么,对于每个询问 ( A , B ) (A,B) (A,B),设其经过上述转换得到的 vector \text{vector} vector V V V,在 V V V后面加上一个数字 3 3 3得到 V ′ V' V,那么,这个询问的答案就是字典序小于 V ′ V' V vector \text{vector} vector的数量减去字典序小于 V V V vector \text{vector} vector的数量。

在求每个二元组的 vector \text{vector} vector时,我们可以将连续的相同的一段数字压缩起来,这样可以让 A A A B B B求差的操作变为求模的操作,还可以使 vector \text{vector} vector更简短。可以保证, vector \text{vector} vector的长度是 O ( log ⁡ m x ) O(\log mx) O(logmx)的,其中 m x mx mx a i , b i , A , B a_i,b_i,A,B ai,bi,A,B的最大值。

时间复杂度为 O ( ( n + q ) log ⁡ ( n + q ) log ⁡ m x ) O((n+q)\log(n+q)\log mx) O((n+q)log(n+q)logmx)

code

#include<bits/stdc++.h>
using namespace std;
typedef vector<long long> V;
int T,n,q,v1,sum,ans[500005];
struct node{
	V s;
	int t;
}v[2000005];
int pd(const V &a,const V &b){
	if(a[0]!=b[0]) return a[0]<b[0]?-1:1;
	int s1=a.size(),s2=b.size();
	for(int i=1;i<s1&&i<s2;i+=2){
		if(a[i]!=b[i]) return a[i]<b[i]?-1:1;
		if(!a[i]) return 0;
		if(a[i+1]<b[i+1]){
			if(i+2>=s1) return -1;
			return a[i+2]<b[i]?-1:1;
		}
		if(b[i+1]<a[i+1]){
			if(i+2>=s2) return 1;
			return a[i]<b[i+2]?-1:1;
		}
	}
	if(s1!=s2) return s1<s2?-1:1;
	return 0;
}
bool cmp(const node &a,const node &b){
	int fl=pd(a.s,b.s);
	if(fl) return fl<0;
	return a.t<b.t;
}
V gt(long long x,long long y){
	V re;
	long long vt;
	while(x!=y){
		if(x<y){
			vt=y;y%=x;
			if(!y) y=x;
			re.push_back((vt-y)/x);
			re.push_back(1);
		}
		else{
			vt=x;x%=y;
			if(!x) x=y;
			re.push_back((vt-x)/y);
			re.push_back(2);
		}
	}
	re.push_back(x);
	reverse(re.begin(),re.end());
	return re;
}
int main()
{
	scanf("%d",&T);
	while(T--){
		long long x,y;
		scanf("%d%d",&n,&q);
		v1=0;
		for(int i=1;i<=n;i++){
			scanf("%lld%lld",&x,&y);
			v[++v1].t=0;
			v[v1].s=gt(x,y);
		}
		for(int i=1;i<=q;i++){
			ans[i]=0;
			scanf("%lld%lld",&x,&y);
			V s=gt(x,y);
			v[++v1].t=-i;
			v[v1].s=s;
			s.push_back(3);
			v[++v1].t=i;
			v[v1].s=s;
		}
		sort(v+1,v+v1+1,cmp);
		sum=0;
		for(int i=1,x;i<=v1;i++){
			x=v[i].t;
			if(!x) ++sum;
			else if(x>0) ans[x]+=sum;
			else ans[-x]-=sum;
		}
		for(int i=1;i<=q;i++){
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值