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} 1≤T≤100,1≤n,q≤5×104,1≤ai,bi,A,B≤1018
数据保证 ∑ n ≤ 5 × 1 0 5 , ∑ q ≤ 5 × 1 0 5 \sum n\leq 5\times 10^5,\sum q\leq 5\times 10^5 ∑n≤5×105,∑q≤5×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,B−A)
- 当 A > B A>B A>B时, ( A , B ) (A,B) (A,B)的前驱二元组为 ( A − B , B ) (A-B,B) (A−B,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;
}