NKOJ 3984 (WC 2010)重建計劃(二分答案+點分治+單調dp)

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,那麼原題變成求是否存在一條長度在[L,R] 之間,且權值和>=0 的路徑。

樹上路徑問題,考慮點分治,考慮過分治中心的路徑,按子樹順序討論,記錄F[d] 表示當前子樹之前的子樹深度爲d 的最大路徑的權,G[d] 表示當前子樹深度爲d 的路徑的最大權,那麼可以dp,從小到大枚舉d,用G[d]F[Ld]F[Rd] 來更新答案,顯然可以用單調隊列來優化。dp完了之後用G 更新F ,繼續討論下一棵子樹。

這裏理論上需要將子樹按照最大深度排序來保證複雜度。具體不好說。

另外需要加上一些剪枝,即當前子樹點數小於L就不用分治下去了。
另外,最好每層點分治單獨二分,這樣會跑得比較快。
但是我就是在外面二分,這樣就需要加上一些其他優化才能卡過了,比如找到答案就不繼續分治,預處理點分治樹而不要每次都重新建立點分治樹之類。

總時間複雜度O(nlog2n)


代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章