题意:
有一棵树,n个点,边长为1,m个询问。每个询问(x,y,u,v)表示一个人从x开始,不停的在(x,y)间往返跑,另一个人从u开始在(u,v)间往返跑,两人每秒的速度都是1,问什么时候第一次在某个节点上相遇。
n,m<=200000
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 210000
#define maxd 18
#define LL long long
using namespace std;
struct node{int y,nex;}a[2*N];
struct node1{int dep,par[20];}t[N];
int fir[N],len,n,m,z;
LL inf=1ll<<60;
bool bo;
void ins(int x,int y)
{
a[++len].y=y;a[len].nex=fir[x];fir[x]=len;
}
void dfs(int x,int fa)
{
t[x].dep=t[fa].dep+1;t[x].par[0]=fa;
for(int i=1;i<=maxd;i++) t[x].par[i]=t[t[x].par[i-1]].par[i-1];
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(y==fa) continue;
dfs(y,x);
}
}
int lca(int x,int y)
{
if(t[x].dep<t[y].dep) swap(x,y);
for(int i=maxd;i>=0;i--) if(t[t[x].par[i]].dep>=t[y].dep) x=t[x].par[i];
if(x==y) return x;
for(int i=maxd;i>=0;i--) if(t[x].par[i]!=t[y].par[i]) x=t[x].par[i],y=t[y].par[i];
return t[x].par[0];
}
int lower(int x,int y)
{
if(t[x].dep>t[y].dep) return x;
return y;
}
int dis(int x,int y)
{
int u=lca(x,y);
return t[x].dep+t[y].dep-2*t[u].dep;
}
int exgcd(int a,int b,LL &x,LL &y)
{
if(b==0) {x=1;y=0;return a;}
LL t=a/b,g,xx,yy;
g=exgcd(b,a%b,xx,yy);
x=yy;y=xx-t*yy;
return g;
}
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a%b);
}
LL same(int t1,int t2,int f1,int f2)
{
int t=((t2-t1)%f2+f2)%f2;LL p,q,g,m;
g=exgcd(f1,f2,p,q);
if(t%g) return inf;
p*=t/g;q*=t/g;
m=f2/g;
p=(p%m+m)%m;
return p*f1+t1;
}
int G(LL M,LL D,LL L,LL R)
{
LL t=L/D;
if(t*D<L) t++;
if(t*D<=R) return t;
if(2*D>M) return G(M,M-D,M-R,M-L);
int k=G(D,((-M%D)+D)%D,L%D,R%D);
L+=M*k;R+=M*k;
t=L/D;
if(t*D<L) t++;
return t;
}
LL make(int M,int D,int L,int R)
{
int g=gcd(M,D);
if((L-1)/g>=R/g) return inf;
return G(M,D,L,R);
}
LL diff(int t1,int t2,int f1,int f2,int d)
{
int l=t2-t1-d,r=t2-t1+d;
l=(l%f2+f2)%f2;
r=(r%f2+f2)%f2;
if(l%2) return inf;
if(l>r || l==0) return t1+r/2;
if(f2==d*2) return t1+r/2;
LL p=make(f2,f1%f2,l,r),q;
if(p==inf) return inf;
return p*f1+t1+d-(p*f1%f2-l)/2;
}
LL solve()
{
int x,y,u,v,a,b,p1,p2,p3,p4,p5,p6,p,d,t1,t2,t3,t4,f1,f2,d1,d2;
z++;
scanf("%d%d%d%d",&x,&y,&u,&v);
if(bo && z==143) printf("%d %d %d %d\n",x,y,u,v);
p1=lca(x,y);p2=lca(u,v);p3=lca(x,u);p4=lca(x,v);p5=lca(y,u);p6=lca(y,v);
p=lower(p1,p2);a=lower(p3,p4);b=lower(p5,p6);
if(t[p].dep>t[a].dep && t[p].dep>t[b].dep) return -1;
if(t[p].dep>t[a].dep) a=p;
else if(t[p].dep>t[b].dep) b=p;
d=dis(a,b);f1=2*dis(x,y);f2=2*dis(u,v);
d1=dis(x,a);d2=dis(x,b);
if(d1<d2) t1=d1,t2=f1-d2;
else t1=f1-d1,t2=d2;
d1=dis(u,a);d2=dis(u,b);
if(d1<d2) t3=d1,t4=f2-d2;
else t3=f2-d1,t4=d2;
LL res=inf;
res=min(same(t1,t3,f1,f2),res);
res=min(same(t2,t4,f1,f2),res);
res=min(diff(t1,t4,f1,f2,d),res);
res=min(diff(t2,t3,f1,f2,d),res);
if(res==inf) res=-1;
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(1,0);
scanf("%d",&m);
//if(m==8100 && a[1].y==1) bo=1;
while(m--)
{
LL ans=solve();
if(bo==0) printf("%lld\n",ans);
}
return 0;
}
题解:
看懂题解后几乎抄了一遍标程
有点复杂,官方题解很清楚,这里不赘述了。
这题一个核心子问题,也是有通用性的就是给出M,D,L,R,询问最小的m满足:L<=D*m<=R(%M)
设G(M,D,L,R)表示答案。
由于D*m%M是gcd(D,M)的倍数,所以无解条件是
L−1(D,M)>=R(D,M)
如果去掉取模有解,则容易找出最优解
当2D>M时,把每次加D,范围[L,R],看成每次减D,范围在[-L,-R]
这样问题就变成G(M,M-D,M-R,M-L)。注意变换一次后即有2D< M。
当2D< M时,有
L+M*k<=D*m<=R+M*k
L<=D*m-M*k<=R
若能求出一个最小的k,使m存在,那此时m也是最小的
注意L和R之间不存在D的倍数,所以可以在模D的意义下看待这个方程,即
L%D<=-M%D*k<=R%D(%D)
所以k的值就是G(D,D-M%D,L%D,R%D)
注意此时问题规模减小一半,故复杂度
O(logn)