BZOJ1758 WC2010 重建計劃

Problem

BZOJ

Solution

二分答案,把路徑權值都減去mid,那麼就變成了能不能選出一條長度[L,R][L,R]權值大於等於0的路徑。
考慮點分治,那麼我們就只需要考慮拼接兩棵子樹。這是一個有長度限制以深度爲下標的dp。這裏用單調隊列優化。記錄 f[i]f[i] 表示之前遍歷過的子樹中深度爲 ii 到根的最大權值,g[i]g[i] 爲當前的,單調隊列優化拼接即可。

但是仔細想想,如果 ff 如果很長,gg 卻很短,如此構造可以使點分治退化成O(n2logn)O(n^2\log n)。爲了複雜度正確,必須使f,gf,g的長度與當前處理的子樹大小相關,那麼我們按子樹最深的深度從小到大處理。這樣的複雜度就不會退化。

時間複雜度O(nlognlogv)O(n\log n \log v)

然後卡常纔是這道題的噁心之處。筆者的代碼用了這麼些卡常的方法:

  • 預處理出點分樹,並把子樹先排序好,用vector存起來
  • 減少調用dfs
  • 當前樹的大小小於等於 LL 時直接退出
  • 減少實數運算

由於預處理了點分樹,所以你還可以通過逆序枚舉分治重心來應對毒瘤數據。

Code

#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>
#define mk(x,y) make_pair(x,y)
#define fr first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=100010;
const double eps=1e-7,INF=1e9;
template <typename Tp> int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> void read(Tp &x)
{
	x=0;char ch=getchar();int f=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') ch=getchar(),f=1;
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();
	if(f) x=-x;
}
struct data{
	int v,nxt;double w;
	data(const int _v=0,const double _w=0.0,const int _nxt=0){v=_v;w=_w;nxt=_nxt;}
}edge[maxn<<1];
int n,L,R,p,lim,now,head[maxn],dep[maxn],dsz[maxn],tp[maxn];
double ans,f[maxn],g[maxn],w[maxn];
deque<int> q;
vector<pii> son[maxn];
vector<pii>::iterator itr;
namespace Build{
	const static int INF=0x3f3f3f3f;
	int p,sn,rt,top,sz[maxn],mx[maxn],dis[maxn],mark[maxn],stk[maxn];
	int cmp(const int &a,const int &b){return dis[a]<dis[b];}
	void getrt(int x,int pre)
	{
		sz[x]=1;mx[x]=dis[x]=0;
		for(int i=head[x];i;i=edge[i].nxt)
		  if(!mark[edge[i].v]&&edge[i].v^pre)
		  {
		  	getrt(edge[i].v,x);
		  	sz[x]+=sz[edge[i].v];
		  	getmax(mx[x],sz[edge[i].v]);
		  	getmax(dis[x],dis[edge[i].v]);
		  }
		getmax(mx[x],sn-sz[x]);
		if(mx[x]<mx[rt]) rt=x;
		++dis[x];
	}
	void dfs(int x,int now)
	{
		mark[x]=1;getrt(x,x);dep[x]=now;dsz[x]=sz[x];top=0;
		for(int i=head[x];i;i=edge[i].nxt)
		  if(!mark[edge[i].v])
		    stk[++top]=edge[i].v;
		sort(stk+1,stk+top+1,cmp);
		for(int i=1;i<=top;i++) son[x].push_back(mk(stk[i],dis[stk[i]]));
		for(int i=head[x];i;i=edge[i].nxt)
		  if(!mark[edge[i].v])
		  {
		  	sn=sz[edge[i].v];rt=0;
		  	getrt(edge[i].v,edge[i].v);
		  	dfs(rt,now+1);
		  }
	}
}
int cmp(const int &a,const int &b){return dep[a]<dep[b];}
void insert(int u,int v,double w)
{
	edge[++p]=data(v,w,head[u]);head[u]=p;
	edge[++p]=data(u,w,head[v]);head[v]=p;
}
void input()
{
	int u,v,w;
	read(n);read(L);read(R);
	for(int i=1;i<n;i++)
	{
		read(u);read(v);read(w);
		insert(u,v,(double)w);
	}
	Build::mx[0]=Build::INF;Build::sn=n;
	Build::getrt(1,1);Build::dfs(Build::rt,1);
	for(int i=1;i<=n;i++) tp[i]=i;
	sort(tp+1,tp+n+1,cmp);
}
void dfs2(int x,int pre,int deep,double sum)
{
	getmax(g[deep],sum);
	for(int i=head[x];i;i=edge[i].nxt)
	  if(dep[edge[i].v]>now&&edge[i].v^pre)
	    dfs2(edge[i].v,x,deep+1,sum+edge[i].w);
}
int work(int x)
{
	if(dsz[x]<=L) return 0;
	int lf=0,lg,ptr,tot=(int)son[x].size();
	now=dep[x];
	for(int i=head[x];i;i=edge[i].nxt)
	  if(dep[edge[i].v]>dep[x])
	    w[edge[i].v]=edge[i].w;
	itr=son[x].end()-1;
	for(int i=itr->se;i;i--) f[i]=g[i]=-INF;
	for(itr=son[x].begin();itr!=son[x].end();++itr)
	{
		lg=itr->se;dfs2(itr->fr,x,1,w[itr->fr]);ptr=min(lf,R-1);
		if(lf+lg>=L)
		{
			while(!q.empty()) q.pop_front();
			for(int i=1;i<=lg;i++)
			{
				while(ptr&&i+ptr>=L)
				{
					while(!q.empty()&&f[q.back()]<f[ptr]) q.pop_back();
					q.push_back(ptr);--ptr;
				}
				while(!q.empty()&&i+q.front()>R) q.pop_front();
				if(!q.empty()&&g[i]+f[q.front()]>=0) return 1;
			}
		}
		lf=lg;
		for(int j=1;j<=lf;j++) getmax(f[j],g[j]),g[j]=-INF;
	}
	for(int i=min(R,lf);i>=L;i--) if(f[i]>=0) return 1;
	return 0;
}
int check(double x)
{
	for(int i=n;i>=1;i--)
	  if(work(tp[i]))
	    return 1;
	return 0;
}
int main()
{
	input();
	double l=0,r=1e6,mid;
	while(l+1e-4<=r)
	{
		mid=(l+r)/2;
		for(int i=1;i<=p;i++) edge[i].w-=mid;
		if(check(mid)) l=mid,ans=mid;
		else r=mid;
		for(int i=1;i<=p;i++) edge[i].w+=mid;
	}
	printf("%.3lf\n",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章