bzoj 1758: [Wc2010]重建计划
Description
Input
第 一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号
Output
输出最大平均估值,保留三位小数
Sample Input
2 3
1 2 1
1 3 2
1 4 3
Sample Output
HINT
N<=100000,1<=L<=U<=N-1,Vi<=1000000 新加数据一组 By leoly,但未重测..2016.9.27
Source
这个WC题与一道WG的题贼像!!!
然后这题的套路据说又与wfj_2048大佬的middle贼像!!!
这种求平均值和中位数的套路就是二分,具体对于这个题来说二分一个平均值,把所有的边的边权减去mid
剩下的就是判断是否有一条长度在[L,R]间的路权值大于0;也就是说求长度在[L,R]间的权值最大的边
我个傻逼想到这里就GG了,然后就只能堕落了,于是去翻了翻LightGod的博客,Orz,太厉害了
对于一个点,一条路径只有经过这个点,或不经过这个点两种情况,不经过这个点的情况就往下递归,我们考虑经过这个点的情况
对于一条简单路径,他必定是在这个点的子树中选取两条不在同一子树中的路径进行相加
于是这个东西可以用单调队列来维护一个滑动窗口,(ANS[i]记录长度为i的权值最大的路径)
具体做法是这样:
我们考虑这个点的每个儿子管理的连通块(在每个儿子getdis的时候把该儿子管辖下的连通块中每个长度的权值最大值记录下来)
那么我们枚举处理该儿子的记录下来的边(每个长度的权值最大的边),假设长度为i,那么对于其他子树中长度的取值范围就是[L-i,R-i];
然后我们用普通单调队列的处理,处理完后这个儿子后再拿这个儿子的所有边去更新ANS.
对于每个点可以O(size)维护该点的最优值;
然后这个题就打完了...然后调了好久,是一个函数名打错了
附上代码:
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<queue> #include<set> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #define lson num<<1 #define rson num<<1|1 #define deepmaxlen dml using namespace std; typedef long long ll; const int N=500050; const int Inf=2147483647; const double eps=1e-5; int gi() { int x=0,flag=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*flag; } int head[N],to[N],nxt[N]; int size[N],f[N],deep[N],rt[N],vis[N],q[N]; double dml[N],ANS[N],ans=-Inf,c[N],dis[N]; int SIZE,root,cnt,tot,tot2,L,R,M_DEP,n; struct data{ int id,dep; }son[N]; bool cmp(data a,data b){ return a.dep<b.dep; } void lnk(int x,int y,int v){ to[++cnt]=y,c[cnt]=(double)v,nxt[cnt]=head[x],head[x]=cnt; to[++cnt]=x,c[cnt]=(double)v,nxt[cnt]=head[y],head[y]=cnt; } void getroot(int x,int fa){ size[x]=1;f[x]=0; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!vis[y]&&y!=fa){ getroot(y,x); size[x]+=size[y]; f[x]=max(f[x],size[y]); } } f[x]=max(f[x],SIZE-size[x]); if(f[x]<f[root]) root=x; } void get_maxdis(int x,int fa){ dml[deep[x]]=max(dml[deep[x]],dis[x]); for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(y!=fa&&!vis[y]){ dis[y]=dis[x]+c[i]; get_maxdis(y,x); } } } void get_deep(int x,int fa){ ;M_DEP=max(M_DEP,deep[x]); for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(y!=fa&&!vis[y]){ deep[y]=deep[x]+1; get_deep(y,x); } } } void work(int x){ rt[++tot]=x;vis[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!vis[y]){ root=0;SIZE=size[y];getroot(y,0); work(root); } } } void work2(int x){ tot2++;x=rt[tot2];int cnt2=0;vis[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!vis[y]){ dis[y]=c[i]; M_DEP=deep[y]=1;get_deep(y,x); son[++cnt2]=(data){y,M_DEP}; } } int last=0; sort(son+1,son+1+cnt2,cmp); for(int i=1;i<=cnt2;i++){ get_maxdis(son[i].id,x); int st=1,ed=0,l=last,r=last; for(int j=1;j<=son[i].dep;j++){ while(l>=0&&l>=L-j){ while(st<=ed&&ANS[q[ed]]<ANS[l]) ed--; q[++ed]=l--; } for(;r>=0&&r+j>R;r--) while(st<=ed&&q[st]>=r) st++; if(st<=ed) ans=max(ans,dml[j]+ANS[q[st]]); } for(int j=1;j<=son[i].dep;j++) ANS[j]=max(ANS[j],dml[j]),dml[j]=(double)-Inf; last=max(last,son[i].dep); } for(int i=1;i<=last;i++) ANS[i]=(double)-Inf; if(ans>=0) return; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!vis[y]){ work2(y); } } } bool check(double mid){ for(int i=1;i<=cnt;i++) c[i]-=mid; for(int i=1;i<=n;i++) vis[i]=0,dis[i]=0,deep[i]=0;ans=-Inf; tot2=0;work2(rt[1]); for(int i=1;i<=cnt;i++) c[i]+=mid; return ans>=0; } int main(){ n=gi();L=gi();R=gi(); int x,y,z; for(int i=1;i<n;i++){ x=gi(),y=gi(),z=gi();lnk(x,y,z); } for(int i=1;i<=n;i++) dml[i]=ANS[i]=-Inf; root=0;f[0]=Inf;SIZE=n;getroot(1,0);work(root); double l=0,r=1000000; while(r-l>eps){ double mid=(l+r+eps)/2; if(check(mid))l=mid; else r=mid-eps; } printf("%.3lf",l); }