bzoj3219 巡游

http://www.elijahqi.win/2018/01/20/bzoj3219/

Description
Tar国正在准备每年一次的巡游活动。国王将会在一个城市S里召集人群,沿着城市间的道路进行游览,最终在一个城市T里发表他每年一次的著名演讲。
Tar国有N个城市,由于国家的特殊要求,每两个城市之间存在一条唯一的简单通路。
国王希望借着这个机会视察Tar国的城市建设,因此他提出S到T的距离不能少于L条道路。
同时,国王的私人医生检查了他的身体情况后,断定国王的身体不适合做长途旅行,因此他要求S到T的距离不能多于R条道路。
另外,政府希望跟随国王的人民沿途不仅能看到城市风景,还能看到城市外的美丽乡村。因此每条道路定义了一个魅力值Ci,一条路径的魅力值定义为这条路径的中位数。更详细的说法是这样的:
将路径上所有边的魅力值排序,得到序列{Ai}。假设i=2k+c(0<=c<=1),中位数就是A(k+1)。
你的任务就是求出魅力值最大的路径,并输出这个魅力值。
Input
第一行是三个整数N,L,R,表示Tar国的城市个数、路径的最小和最大长度。
接下来N-1行,每行3个整数Ai,Bi,Ci,表示有一条连接Ai和Bi且魅力值Ci的道路。
Output

仅一行,表示最大的魅力值。如果不存在这样的路径,输出-1。
Sample Input
5 1 4

1 2 1

1 3 4

3 4 7

3 5 2

Sample Output
7
HINT

对于100%的数据:N<=100000,1<=L<=R<=N-1,1<=Ci<=1000000000。

Source
树的分治
基本同http://blog.csdn.net/elijahqi/article/details/79094248
注意我每次二分答案之后把大于等于答案的数改成1反之改成-1 如果做到>=0的数我就退出即可 最后验证答案 如果>=0 l=mid+1;else r=mid-1

#include<deque>
#include<cstdio>
#include<algorithm>
#define N 110000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x;
}
struct node{
    int y,z,next;
}data[N<<1];
struct node1{
    int y,size,z;
}qq[N];
int n,L,R,h[N],num,size[N],ff[N],sum,root,dep[N],max1,ans;bool visit[N];
int f[N],g[N],dis[N];
inline void get_root(int x,int fa){
    size[x]=1;ff[x]=0;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (visit[y]||y==fa) continue;
        get_root(y,x);size[x]+=size[y];ff[x]=max(ff[x],size[y]);
    }ff[x]=max(ff[x],sum-size[x]);
    if (ff[root]>ff[x]) root=x;
}
inline bool cmp(node1 a,node1 b){return a.size<b.size;}
inline void dfs(int x,int fa,int mid){
    max1=max(max1,dep[x]);f[dep[x]]=max(dis[x],f[dep[x]]);
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;if (y==fa||visit[y]) continue;
        dis[y]=dis[x]+(z>=mid?1:-1);dep[y]=dep[x]+1;dfs(y,x,mid);
    }
}
inline int check(int x,int mid){
    dep[x]=dis[x]=0;int max_deep=0;int flag=-1;
    for (int i=1;i<=num;++i){
        int y=qq[i].y,z=qq[i].z;max1=0;deque<int>q;f[0]=0;g[0]=0;
        dis[y]=dis[x]+(z>=mid?1:-1);dep[y]=dep[x]+1;dfs(y,x,mid);max_deep=max(max_deep,max1);
        for (int j=max_deep;j>=L;--j) {
            while(!q.empty()&&g[j]>g[q.back()]) q.pop_back();q.push_back(j);}
        for (int j=0;j<=max1;++j){
            while(!q.empty()&&j+q.front()>R) q.pop_front();
            if (!q.empty()) if (f[j]+g[q.front()]>=0) {flag=f[j]+g[q.front()];break;}
            while(!q.empty()&&L-j-1>=0&&g[L-j-1]>g[q.back()]) q.pop_back();q.push_back(L-j-1);
        }
        for (int j=0;j<=max1;++j) g[j]=max(g[j],f[j]),f[j]=-inf;
        if (flag>=0) break;
    }
    for (int i=0;i<=max_deep;++i) g[i]=-inf;return flag;
}
inline void solve(int x){
    if (sum<L) return;visit[x]=1;num=0;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (size[y]>size[x]) size[y]=sum-size[x];
        qq[++num].size=size[y];qq[num].y=y;qq[num].z=z;
    }sort(qq+1,qq+num+1,cmp);int l=ans,r=1e9;
    while(l<=r){
        int mid=l+r>>1;
        if (check(x,mid)>=0) l=mid+1;else r=mid-1;
    }ans=max(r,ans);
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (visit[y]) continue;
        root=0;sum=size[y];get_root(y,x);solve(root);
    } 
}
int main(){
    freopen("bzoj3219.in","r",stdin);
    n=read();L=read();R=read();
    for (int i=0;i<=n;++i) f[i]=g[i]=-inf;
    for (int i=1;i<n;++i){
        int x=read(),y=read(),z=read();
        data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].z=z;data[num].next=h[y];h[y]=num;
    }sum=n;ff[0]=inf;root=0;get_root(1,0);
    solve(root);printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值