P3984[WC2010]重建計劃
問題描述
輸入格式
第一行包含一個正整數N,表示X國的城市個數.
第二行包含兩個正整數L和U,表示政策要求的第一期重建方案中修建道路數的上下限
接下來的N-1行描述重建小組的原有方案,每行三個正整數Ai,Bi,Vi分別表示道路(Ai,Bi),其價值爲Vi 其中城市由1..N進行標號
輸出格式
輸出最大平均估值,保留三位小數
樣例輸入 1
4
2 3
1 2 1
1 3 2
1 4 3
樣例輸出 1
2.500
樣例輸入 2
12
3 5
1 2 804224
1 3 645617
2 4 763931
2 6 744133
2 8 534824
4 5 163318
6 7 421158
6 10 773167
6 11 380598
8 9 639836
9 12 261707
樣例輸出 2
773841.333
提示
20%的數據,N<=5000
30%的數據,N<=100000,原有方案恰好爲一條路徑
100%的數據,N<=100000,1<=L<=U<=N-1,Vi<=1000000
題目中要求的東西帶了個平均值,不好處理,考慮二分答案,然後將每條邊權減去mid,那麼原題變成求是否存在一條長度在 之間,且權值和 的路徑。
樹上路徑問題,考慮點分治,考慮過分治中心的路徑,按子樹順序討論,記錄 表示當前子樹之前的子樹深度爲 的最大路徑的權, 表示當前子樹深度爲 的路徑的最大權,那麼可以dp,從小到大枚舉d,用 與 來更新答案,顯然可以用單調隊列來優化。dp完了之後用 更新 ,繼續討論下一棵子樹。
這裏理論上需要將子樹按照最大深度排序來保證複雜度。具體不好說。
另外需要加上一些剪枝,即當前子樹點數小於L就不用分治下去了。
另外,最好每層點分治單獨二分,這樣會跑得比較快。
但是我就是在外面二分,這樣就需要加上一些其他優化才能卡過了,比如找到答案就不繼續分治,預處理點分治樹而不要每次都重新建立點分治樹之類。
總時間複雜度
代碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<deque>
#include<vector>
#define N 200005
using namespace std;
deque<int>Q;
double Lim;
int n,L,R;
int TOT,LA[N],NE[N],EN[N];
double LE[N],F[2][N];
int Min,rt,Rt,si[N],Md;
bool mark[N];
vector<int>to[N],son[N];
void ADD(int x,int y,double z)
{
TOT++;
EN[TOT]=y;
LE[TOT]=z;
NE[TOT]=LA[x];
LA[x]=TOT;
}
void Gsi(int x,int f)
{
int i,y;si[x]=1;
for(i=LA[x];i;i=NE[i])
{
y=EN[i];
if(mark[y]||y==f)continue;
Gsi(y,x);si[x]+=si[y];
}
}
void Grt(int x,int s,int f)
{
int i,y,Max=0;
for(i=LA[x];i;i=NE[i])
{
y=EN[i];
if(y==f||mark[y])continue;
Grt(y,s,x);
if(si[y]>Max)Max=si[y];
}
if(s-si[x]>Max)Max=s-si[x];
if(Max<Min)Min=Max,rt=x;
}
void Gdis(int x,double s,int d,int f,int ty)
{
if(d>R)return;
F[ty][d]=max(F[ty][d],s);Md=max(Md,d);
for(int i=LA[x];i;i=NE[i])
if(EN[i]!=f&&!mark[EN[i]])Gdis(EN[i],s+LE[i]-Lim,d+1,x,ty);
}
bool MT(int a,int b)
{
int i,j,l=a;
Q.clear();
for(i=1;i<=b;i++)
{
while(l>=0&&i+l>=L)
{
while(Q.size()&&F[0][Q.back()]<=F[0][l])Q.pop_back();
Q.push_back(l);l--;
}
while(Q.size()&&i+Q.front()>R)Q.pop_front();
if(Q.size()&&F[1][i]+F[0][Q.front()]>=0)return 1;
}
return 0;
}
void PreDC(int x)
{
int i,y;mark[x]=1;
for(i=LA[x];i;i=NE[i])
{
y=EN[i];
if(mark[y])continue;
Min=1e9;Gsi(y,0);Grt(y,si[y],0);
si[rt]=si[y];
to[x].push_back(rt);
PreDC(rt);
}
}
bool DC(int x)
{
int i,j,y,las;las=Md=0;mark[x]=1;
if(si[x]<L)return 0;
for(i=LA[x];i;i=NE[i])
{
y=EN[i];
if(mark[y])continue;
las=max(las,Md);Md=0;
Gdis(y,LE[i]-Lim,1,x,1);
if(MT(las,Md))return Md=max(Md,las),1;
Gdis(y,LE[i]-Lim,1,x,0);
for(j=0;j<=Md;j++)F[1][j]=-1e9;
}
for(i=1;i<=las||i<=Md;i++)F[0][i]=-1e9;
for(i=0;i<to[x].size();i++)
{
y=to[x][i];
if(DC(y))return 1;
}
return 0;
}
bool ok(double mid)
{
Lim=mid;
if(!Md)Md=1e5;
memset(mark,0,sizeof(mark));
for(int i=1;i<=Md;i++)F[0][i]=F[1][i]=-1e9;
if(DC(Rt))return 1;
return 0;
}
void EF(double l,double r)
{
double mid;
while(r-l>=0.0001)
{
mid=(l+r)/2;
if(ok(mid))l=mid;
else r=mid;
}
printf("%.3lf",(l+r)/2);
}
int main_main()
{
int i,j,k,x,y,z,Max=0;
scanf("%d%d%d",&n,&L,&R);
for(i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
ADD(x,y,1.0*z);ADD(y,x,1.0*z);Max=max(Max,z);
}
Min=1e9;Gsi(1,0);Grt(1,n,0);
si[rt]=n;Rt=rt;PreDC(rt);
EF(double(0),double(Max));
}
const int main_stack=16;
char my_stack[128<<20];
int main() {
__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
__asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");
main_main();
__asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
return 0;
}