zoukankan      html  css  js  c++  java
  • 「SOL」重建计划(BZOJ)

    期末考完上竞赛,新春佳节上竞赛,元宵大年上竞赛,开学继续上竞赛
    ╰(‵□′)╯


    # 题面

    给定一棵 (n) 个点的边带权的树,要求选出一条边的数量在 (L,R) 之间的简单路径,使得该路径的边权平均值最大。求该最大平均值。

    数据规模:(nle10^5),边权不超过 (10^6)


    # 解析

    平均值问题,不难想到分数规划。于是首先二分出答案,然后给每个边权减去二分值,问题转化为「找出一条边的数量在 (L,R) 之间,边权和大于等于 (0) 的简单路径」。这样的话,其实就相当于求边权和最大的路径,判断其边权是否大于等于 (0)

    树上的路径问题,于是可以想到树分治。不能边分治,因为边分治需要改变树形,重构出的树的路径的边权平均值和原树是不一样的。只能考虑点分治。

    那么就是要统计过重心的路径。考虑依次(可以先想一下依什么次)遍历子树,统计出已经遍历的子树中「到重心边数为 (i) 的路径的最大权」记为 (f_i)。为了避免算到两个端点在同一棵子树中的路径(非简单路径),我们要先用计入子树 (v) 之前的 (f_i) 与子树 (v) 来计算答案后,再将子树 (v) 合并到 (f_i) 中。

    怎么计算答案?我们按路径长度从小到大枚举子树 (v) 的一条路径,不妨记该路径长度为 (i),那么另一条路径长度范围 ([L-i,R-i])。随着 (i) 增大,这个区间是只会往左移动的,而我们要求的就是这个区间中 (f_i) 的最大值,这个问题可以用单调队列解决。

    现在来分析一下时间复杂度,记当前分治的连通块大小为 (S)(f_i) 的长度是 (O(S)) 的,那么每求一次答案需要将 (f_i) 扫一遍,所以复杂度是 (O(S^2)) 的???这与点分治要求的每个连通块内的复杂度 (O(S)) 差得也太多了。

    不难想到,在将子树 (v) 的信息合并到 (f_i) 的过程中,(f_i) 的长度是不断增大的,所以 (f_i) 扫描的起点不从 (S) 开始,而是从当前有值的位置开始。

    但是这样仍然不足够——如果我们遍历的第一棵子树是所有子树中最大的一棵(最大有 (frac{S}{2}),因为是点分治嘛)那么合并信息后 (f_i) 的长度直接就变成了 (O(S)),之后每棵子树都要扫一遍 (f_i),复杂度还是 (O(S^2)) 的。

    所以注意到之前的那个问题「按什么顺序遍历子树?」我们可以聪明一点,先从深度小的开始遍历,类似于启发式的思想(不知道算不算启发式)。再分析复杂度,在计算某一棵子树的答案时,(f_i) 的长度是上一棵子树的深度。总的遍历的复杂度是所有子树深度之和,于是就保证了复杂度是 (O(S)) 的。

    于是最终复杂度 (O(nlog nlog w))(w) 是二分的权值范围)。


    # 源代码

    点击展开/折叠代码
    /*Lucky_Glass*/
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    inline int rin(int &r){
    	int b=1,c=getchar();r=0;
    	while(c<'0' || '9'<c) b=c=='-'?-1:b,c=getchar();
    	while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'),c=getchar();
    	return r*=b;
    }
    const int N=1e5+10;
    #define con(type) const type &
    const double EPS=1e-8;
    typedef pair<int,int> pii;
    
    struct SuperDeque{
    	int ary[N],le,ri;
    	void clear(){le=1,ri=0;}
    	void push_back(con(int)x){ary[++ri]=x;}
    	void pop_back(){ri--;}
    	void pop_front(){le++;}
    	int back(){return ary[ri];}
    	int front(){return ary[le];}
    	int size(){return ri-le+1;}
    }que;
    
    int n,bonl,bonr,rsiz,rwei,rroot;
    int siz[N],hgt[N];
    double val_bas;
    double val_bin[N],tmp_bin[N];
    bool ban[N],ifsort[N];
    vector<pii> lnk[N];
    
    int findCenter(con(int)u,con(int)fa){
    	int maxsizv=0,sizu=1;
    	for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
    		int v=lnk[u][it].first;
    		if(v==fa || ban[v]) continue;
    		int sizv=findCenter(v,u);
    		maxsizv=max(maxsizv,sizv);
    		sizu+=sizv;
    	}
    	int weiu=max(rsiz-sizu,maxsizv);
    	if(weiu<rwei) rwei=weiu,rroot=u;
    	return sizu;
    }
    int dataDFS(con(int)u,con(int)fa,con(double)val,con(int)dep){
    	int ret=0;
    	siz[u]=1;
    	tmp_bin[dep]=max(tmp_bin[dep],val);
    	for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
    		int v=lnk[u][it].first;
    		if(v==fa || ban[v]) continue;
    		ret=max(ret,dataDFS(v,u,val+lnk[u][it].second-val_bas,dep+1));
    		siz[u]+=siz[v];
    	}
    	return ret+1;
    }
    int highDFS(con(int)u,con(int)fa){
    	int ret=0;
    	for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
    		int v=lnk[u][it].first;
    		if(v==fa || ban[v]) continue;
    		ret=max(ret,highDFS(v,u));
    	}
    	return ret+1;
    }
    bool cmp(con(pii)a,con(pii)b){return hgt[a.first]<hgt[b.first];}
    bool ina_abs(con(double)key){return key<0?-key:key;}
    int sgn(con(double)key){return ina_abs(key)<EPS?0:(key<0?-1:1);}
    bool dacDFS(int u,con(int)totsiz){
    	ban[u]=true;
    	if(!ifsort[u]){
    		for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
    			int v=lnk[u][it].first;
    			if(ban[v]) continue;
    			hgt[v]=highDFS(v,u);
    		}
    		sort(lnk[u].begin(),lnk[u].end(),cmp);
    		ifsort[u]=true;
    	}
    	fill(val_bin,val_bin+totsiz+1,-1e9),val_bin[0]=0;
    	int ardlen=0;
    	for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
    		int v=lnk[u][it].first;
    		if(ban[v]) continue;
    		int hgtv=dataDFS(v,0,lnk[u][it].second-val_bas,1);
    		que.clear();
    		for(int i=1,j=ardlen;i<=hgtv;i++){
    			while(j>=bonl-i && j>=0){
    				while(que.size() && sgn(val_bin[que.back()]-val_bin[j])<=0)
    					que.pop_back();
    				que.push_back(j);
    				j--;
    			}
    			while(que.size() && que.front()>bonr-i) que.pop_front();
    			if(que.size() && sgn(val_bin[que.front()]+tmp_bin[i])>=0)
    				return true;
    		}
    		for(int i=1;i<=hgtv;i++){
    			val_bin[i]=max(val_bin[i],tmp_bin[i]);
    			tmp_bin[i]=-1e9;
    		}
    		ardlen=hgtv;
    	}
    	for(int it=0,itt=(int)lnk[u].size();it<itt;it++){
    		int v=lnk[u][it].first;
    		if(ban[v]) continue;
    		rwei=1e9,rsiz=siz[v];
    		findCenter(v,0);
    		if(dacDFS(rroot,siz[v])) return true;
    	}
    	return false;
    }
    bool check(con(double)mmi){
    	fill(tmp_bin,tmp_bin+1+n,-1e9);
    	for(int i=1;i<=n;i++) ban[i]=false;
    	val_bas=mmi;
    	rsiz=n,rwei=1e9,findCenter(1,0);
    	return dacDFS(rroot,n);
    }
    int main(){
    	rin(n),rin(bonl),rin(bonr);
    	for(int i=1,u,v,l;i<n;i++){
    		rin(u),rin(v),rin(l);
    		lnk[u].emplace_back(v,l);
    		lnk[v].emplace_back(u,l);
    	}
    	double lle=0,rri=1e6,mmi;
    	while(lle+1e-4<rri){
    		mmi=(lle+rri)/2;
    		if(check(mmi)) lle=val_bas;
    		else rri=val_bas;
    	}
    	printf("%.3f
    ",lle);
    	return 0;
    }
    

    THE END

    Thanks for reading!

    进退之间觉醒 逆转之力
    被黑暗俘虏 想让我沉溺
    绝不会认输 信己不信命
    崩坏的秩序 打破既定的结局

    ——《陷落之序》By 著小生zoki

    > Link 陷落之序-Bilibili

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    7.Layout布局(tabs、accordion、layout)
    6.form表单四种提交方式
    5.form表单验证
    4.easyloader.js文件的作用
    3.window窗口
    2.panel面板
    1.messager消息提示框
    2017-10-5-Python
    2017-9-24-Linux移植:ubuntu server 16.04无法联网&无法apt-get update解决
    2017-9-17-EDFA
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14451360.html
Copyright © 2011-2022 走看看