ZZH的游戏
ZZH的游戏
题解
其实是很简单的一道题
很容易发现一个结论,当一个人开始移动时,他如果未把当前点移动到一个比它的起点小的点是绝不罢休的。很容易证明,毕竟如果这样的话它在移动过程中产生的最大权值是一定不小于先移动另一个点产生的权值的。
于是,我们考虑如何维护当前点下面应该移动到哪一个点。
我们可以先二分出一个最大权值,如果存在一个点比当前点的权值小,且其路径上的最大值加上另外一个点的权值不超过我们的限制,我们就一定可以将其移动。可是关于每个点的路径又应该如何求出呢?
笔者考场上就死在这一点上了。
其实我们可以发现,由于另外一个点现在的权值是不会超过其初始点的权值,故我们从起点开始到目标点上的路径的最大值加上另一个点的权值也一定需要小于我们的限制。
所以,我们可以先bfs一遍将所有点的路径上的最大值求出,二分最大值,再通过指针的方式来一直移动两个点,到达它们现在能到的最小点即可。
时间复杂度
O
(
T
n
l
o
g
n
)
O\left(Tnlog_{n}\right)
O(Tnlogn)。
源码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
#define MAXN 1000005
typedef long long LL;
typedef pair<int,int> pii;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int das[MAXN],T,n,ss,tt;
queue<int> q;
struct edge{int to,nxt;};
bool cmp(int x,int y){return das[x]<das[y];}
class Tree{
private:
int head[MAXN],tot;bool vis[MAXN];edge e[MAXN<<1];
public:
int ord[MAXN],dis[MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void work(int x){
while(!q.empty())q.pop();
for(int i=1;i<=n;i++)vis[i]=0,ord[i]=i,dis[i]=0;
dis[x]=x;vis[x]=1;q.push(x);
while(!q.empty()){
int t=q.front();q.pop();
for(int i=head[t];i;i=e[i].nxt){
int v=e[i].to;if(vis[v])continue;
vis[v]=1;dis[v]=max(dis[t],v);q.push(v);
}
}
for(int i=1;i<=n;i++)das[i]=dis[i];
sort(ord+1,ord+n+1,cmp);
}
void clear(){tot=0;for(int i=1;i<=n;i++)head[i]=0;}
}Ta,Tb;
bool check(int x){
int k1=1,min1=Ta.ord[1],k2=1,min2=Tb.ord[1];
while(k1<n||k2<n){
int lask1=k1,lask2=k2;
while(k1<n&&Ta.dis[Ta.ord[k1+1]]+min2<=x)k1++,min1=min(Ta.ord[k1],min1);
while(k2<n&&Tb.dis[Tb.ord[k2+1]]+min1<=x)k2++,min2=min(Tb.ord[k2],min2);
if(k1==lask1&&k2==lask2)break;
}
return min1==1&&min2==1;
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
read(T);
while(T--){
read(n);
for(int i=1;i<n;i++){
int u,v;read(u);read(v);
Ta.addEdge(u,v);Ta.addEdge(v,u);
}
for(int i=1;i<n;i++){
int u,v;read(u);read(v);
Tb.addEdge(u,v);Tb.addEdge(v,u);
}
read(ss);read(tt);Ta.work(ss);Tb.work(tt);
int l=ss+tt,r=n+min(ss,tt),num=r;
while(l<=r){
int mid=l+r>>1;
if(check(mid))r=mid-1,num=mid;
else l=mid+1;
}
printf("%d\n",num);
Ta.clear();Tb.clear();
}
return 0;
}