BZOJ 1758: [Wc2010]重建计划(点分治+迭代)

92 篇文章 0 订阅
47 篇文章 0 订阅

题目
不敢挂LG。点分治常数太大洛谷上过不了。。。。
1.直接求边数为 l l l的路径的最大路径长度,这个点分治做不了。
因为合并答案的时候是 O ( n 2 ) O(n^2) O(n2)跑不掉的。
唯一的特例是你写FFT。
和 和 卷积?不存在的。
2.二分答案判定有无 [ L , U ] [L,U] [L,U]的路径长度大于边数*二分的答案。
在子树合并时相当于从小到大枚举一个子树深度,另一个子树深度的上限和下限都下降,求最大值,可以用单调队列。
3.需要把子树的深度从小到大排序,这样每次合并的复杂度就是新加入的子树高度,总复杂度 O ( n ) O(n) O(n)
4.为啥 O ( n log ⁡ 2 n ) O(n \log ^2 n) O(nlog2n)的算法跑 1 0 5 10^5 105还这么吃力啊,实数二分答案的常数果然还是太大了。

#include<bits/stdc++.h>
#define maxn 100005
#define LG 20
#define inf 1e15
#define F first
#define S second
#define eps 1e-4
using namespace std;

char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

int n,Lb,Ub;
int dep[maxn],siz[maxn],zdep[maxn];
vector<pair<int,int> >G[maxn];

int rt , Min;
void dfs(int now,int ff,int tsz,int lim){
	int Max = 0;siz[now] = 1;
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if((v=G[now][i].F)!=ff && dep[v]>=lim)
			dfs(v,now,tsz,lim),
			siz[now] += siz[v],
			Max = max(Max , siz[v]);
	Max = max(Max , tsz - siz[now]);
	if(Min > Max) Min = Max , rt = now;
}

int findrt(int now,int tsz,int lim){
	rt = -1 , Min = 0x3f3f3f3f;
	dfs(now , 0 , tsz , lim);
	return rt;
}

void serdep(int now,int ff,int lim,int Dep){
	zdep[now] = Dep , siz[now] = 1;
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F]>lim && v!=ff){
			serdep(v,now,lim,Dep+1);
			zdep[now] = max(zdep[now] , zdep[v]);
			siz[now] += siz[v];
		}
}

bool cmp(const pair<int,int> &u,const pair<int,int> &v){ return zdep[u.F] < zdep[v.F]; }  

void Solve(int now,int lim){
	dep[now] = lim;
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F] > lim)
			serdep(v,now,lim,1);
	sort(G[now].begin(),G[now].end(),cmp);
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F]>lim)
			Solve(findrt(v,siz[v],lim+1),lim+1);
}

double dis[2][maxn];
int l1 , l2;

void ser(int now,int ff,int lim,double dist,int Dep,double ct){
	if(Dep > Ub) return;
	dis[1][Dep] = max(dis[1][Dep] , dist);
	l2 = max(l2 , Dep);
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F] > lim && v!=ff)
			ser(v,now,lim,dist+G[now][i].S-ct,Dep+1,ct);
}


bool check(double mid){
	static int q[maxn],L,R;
	for(int i=0;i<=n;i++) dis[0][i] = dis[1][i] = -inf;
	for(int i=1;i<=n;i++){
		for(int j=l1;j>=0;j--) dis[0][j] = -inf; 
		l1 = dis[0][0] = 0;
		for(int j=0,sz=G[i].size(),v;j<sz;j++)
			if(dep[v=G[i][j].F] > dep[i]){
				for(int k=l2;k>=0;k--) dis[1][k] = -inf; 
				l2 = 0;
				ser(v,i,dep[i],G[i][j].S-mid,1,mid);
				L = R = 0;
				for(int p1=1,p2=l1;p2>=0;p2--){
					for(;p1<=l2 && p1+p2<=Ub;p1++){
						for(;L<R && dis[1][q[R-1]] < dis[1][p1];R--);
						q[R++] = p1;
					}
					for(;L<R && q[L]+p2<Lb;L++);
					if(L<R && dis[1][q[L]] + dis[0][p2] >= 0)
						return 1;
				}
				for(int k=1;k<=l2;k++) 
					dis[0][k] = max(dis[0][k] , dis[1][k]);
				l1 = l2;
			}
	}
	return 0;
}

int main(){
	//freopen("1.in","r",stdin);
	memset(dep,0x3f,sizeof dep);
	read(n),read(Lb),read(Ub);
	for(int i=1;i<n;i++){
		int u,v,w;
		read(u),read(v),read(w);
		G[u].push_back(make_pair(v,w));
		G[v].push_back(make_pair(u,w));
	}
	Solve(findrt(1,n,1),1);
	
	double L=0 , R=1000000,mid;
	for(;L<R-eps;){
		mid = (L+R) * 0.5;
		if(check(mid)) L = mid;
		else R = mid - eps;
	}
	printf("%.3lf\n",L);
}

upd:对着LG Rank1的代码写了分迭代,快的亲妈都不认识了。。。。。。
实测对于随机数据迭代次数基本等于1。
AC Code:

#include<bits/stdc++.h>
#define maxn 100005
#define LG 20
#define inf 1e15
#define F first
#define S second
#define eps 1e-4
using namespace std;

char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

int n,Lb,Ub;
int dep[maxn],siz[maxn],zdep[maxn];
vector<pair<int,int> >G[maxn];

int rt , Min;
void dfs(int now,int ff,int tsz,int lim){
	int Max = 0;siz[now] = 1;
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if((v=G[now][i].F)!=ff && dep[v]>=lim)
			dfs(v,now,tsz,lim),
			siz[now] += siz[v],
			Max = max(Max , siz[v]);
	Max = max(Max , tsz - siz[now]);
	if(Min > Max) Min = Max , rt = now;
}

int findrt(int now,int tsz,int lim){
	rt = -1 , Min = 0x3f3f3f3f;
	dfs(now , 0 , tsz , lim);
	return rt;
}

void serdep(int now,int ff,int lim,int Dep){
	zdep[now] = Dep , siz[now] = 1;
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F]>lim && v!=ff){
			serdep(v,now,lim,Dep+1);
			zdep[now] = max(zdep[now] , zdep[v]);
			siz[now] += siz[v];
		}
}

bool cmp(const pair<int,int> &u,const pair<int,int> &v){ return zdep[u.F] < zdep[v.F]; }  

void Solve(int now,int lim){
	dep[now] = lim;
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F] > lim)
			serdep(v,now,lim,1);
	sort(G[now].begin(),G[now].end(),cmp);
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F]>lim)
			Solve(findrt(v,siz[v],lim+1),lim+1);
}

double dis[2][maxn];
int l1 , l2;

void ser(int now,int ff,int lim,double dist,int Dep,double ct){
	dis[1][Dep] = max(dis[1][Dep] , dist);
	l2 = max(l2 , Dep);
	for(int i=0,sz=G[now].size(),v;i<sz;i++)
		if(dep[v=G[now][i].F] > lim && v!=ff)
			ser(v,now,lim,dist+G[now][i].S-ct,Dep+1,ct);
}

double Ans;
void check(double mid){
	Ans = -inf;
	static int q[maxn],L,R;
	for(int i=0;i<=n;i++) dis[0][i] = dis[1][i] = -inf;
	for(int i=1;i<=n;i++){
		for(int j=l1;j>=0;j--) dis[0][j] = -inf; 
		l1 = dis[0][0] = 0;
		for(int j=0,sz=G[i].size(),v;j<sz;j++)
			if(dep[v=G[i][j].F] > dep[i]){
				for(int k=l2;k>=0;k--) dis[1][k] = -inf; 
				l2 = 0;
				ser(v,i,dep[i],G[i][j].S-mid,1,mid);
				L = R = 0;
				for(int p1=1,p2=l1;p2>=0;p2--){
					for(;p1<=l2 && p1+p2<=Ub;p1++){
						for(;L<R && dis[1][q[R-1]] < dis[1][p1];R--);
						q[R++] = p1;
					}
					for(;L<R && q[L]+p2<Lb;L++);
					if(L<R)
						Ans = max(Ans , (dis[1][q[L]] + dis[0][p2] + mid * (p2 +q[L])) / (p2 + q[L]));
				}
				for(int k=1;k<=l2;k++) 
					dis[0][k] = max(dis[0][k] , dis[1][k]);
				l1 = l2;
			}
	}
}

int main(){
	freopen("1.in","r",stdin);
	memset(dep,0x3f,sizeof dep);
	read(n),read(Lb),read(Ub);
	for(int i=1;i<n;i++){
		int u,v,w;
		read(u),read(v),read(w);
		G[u].push_back(make_pair(v,w));
		G[v].push_back(make_pair(u,w));
	}
	Solve(findrt(1,n,1),1);
	
	double mid=500000;
	for(;;){
		check(mid);
		printf("%.3lf %.3lf\n",mid,Ans);
		if(fabs(mid - Ans) < 1e-4) break;
		mid = Ans;
	}
	printf("%.3lf\n",Ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值