Description
有n个数,执行n-1次合并。
每次合并两个数所在的集合并进行一次询问。
把这个合并后的集合里的数按照数轴上的顺序排序,你可以任意交换相邻的两个数,时间为他们之间的距离。
交换可以同时进行,一个数在进行交换时不能被选择。
当最小值和最大值进行交换时,时间为距离/2并且询问结束。
求最小时间。
n<=50000
Solution
首先,一次询问的答案是,对于相邻的两个数aj和aj+1,那么把a1换到aj,把an换到aj+1,再把他们两个交换是最优策略。
于是我们可以枚举j,Ans=min(max(aj-a1,an-aj+1)+(aj+1-aj)/2)
也就是min(max((aj+1-aj)/2-a1,an-(aj+1-aj)/2))
储存每两个数的中点,那么最接近a1,an中点的那个区间就是最优解。
这个也可以用二分实现。
至于合并,你可以写一个线段树合并,也可以用启发式合并两个set。(black technology)
Code
#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 50005
using namespace std;
typedef double db;
typedef set<int> ::iterator it;
set<int> s[N];
int f[N],a[N],n,x,y,ty;
int get(int x) {return (f[x]-x)?f[x]=get(f[x]):x;}
bool cmp(int x,int y) {}
int merge(int x,int y) {
int a=get(x),b=get(y);
if (s[a].size()<s[b].size()) swap(a,b);
for(it i=s[b].begin();i!=s[b].end();i++) s[a].insert(*i);f[b]=a;return a;
}
int main() {
for(scanf("%d",&ty);ty;ty--) {
scanf("%d",&n);
fo(i,1,n) f[i]=i,s[i].clear(),s[i].insert(i);
fo(i,1,n-1) {
scanf("%d%d",&x,&y);
int id=merge(x,y);it st=s[id].begin(),ed=s[id].end();ed--;
it bz=s[id].lower_bound(ceil((*st+*ed)/2.0));
int r=*bz;bz--;int l=*bz;
db m=(l+r)/2.0;db ans=max(m-*st,*ed-m);
l=r;bz++;bz++;r=*bz;
if (bz!=s[id].end()) {
m=(l+r)/2.0;
ans=min(ans,max(m-*st,*ed-m));
}
bz--;bz--;r=*bz;bz--;l=*bz;
if (r!=*st) {
m=(l+r)/2.0;
ans=min(ans,max(m-*st,*ed-m));
}
printf("%.1lf\n",ans);
}
}
}