【BZOJ 4326】【NOIP 2015 d2t3】运输计划

BZOJ 4326

题意

给你一棵树和边权,有若干条航线,从树上的一个点到另一个点。令运输时间为所有航线所需要的时间中的最长时间。你可以将某一条边的权值变为0,求这个最长时间的最小值。

样例输入

6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5

样例输出

11

SOL

先讲一下部分分毕竟这是一道联赛题。
1、首先想到就是预处理lca,然后枚举变为0的边计算每条航线的权值,时间复杂度为n^2,可以过掉10个点。
2、考虑到m=1只有一条航线,直接找链上的最大值即可,这又有2个点。
3、对于单链的情况,首先这个题目的问法就是让你二分答案。然后我们考虑如何check一个答案。我们假设check的答案为x,预处理每条航线的长度,然后把所有大于x的航线全部放到单链上,如果存在这样一条边,使得所有大于x的航线都经过,并且最长的航线减去这条边的权值小于等于x,这就意味着如果把这条边的权值变为0,所有的航线长度都是小于等于x的,那么这个x就是可行的。这里“把所有大于x的航线全部放到单链上”用到了差分的方法,每次只要对起点和终点操作,时间复杂度为nlogn。这样可以过掉8个点。
结合1、2、3可以过掉16个点。
4、其实上面已经说了单链的做法,那么只要对树剖分一下就可以满分了。最后一个点略微卡常,我用了读入优化后在bzoj和uoj上都是跑得过去的,不过ccf的老爷机听说要被卡掉一个点。

代码

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1000000000
#define mod 1000000007
#define N 350000
using namespace std;
struct tree1
{
    int x,y,val;
} e[N];
struct tree2
{
    int val;
} tree[N*4];
struct qry{int x,y,rate;} q[N];
vector<int> v[N];
int dep[N],siz[N],fa[N],id[N],son[N],val[N],top[N];
int a[N],d[N];
int n,qq,i,num,x,y,l,r,mid;
int res,mx;

int read(){    
    int x=0; char ch=getchar();    
    while (ch<'0' || ch>'9') ch=getchar();    
    while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }    
    return x;    
}    
bool cmp(const qry &a,const qry &b) {return a.rate>b.rate;}
void dfs1(int x,int ftr,int d)
{
    dep[x] = d; siz[x] = 1; son[x] = 0; fa[x] = ftr;
    for (int i = 0;i < v[x].size(); i++)
        {
            int nxt = v[x][i];
            if (nxt == ftr) continue;
            dfs1(nxt,x,d+1);
            siz[x] += siz[nxt];
            if (siz[nxt] > siz[son[x]]) son[x] = nxt;
        }
}
void dfs2(int x,int tp)
{
    top[x] = tp; id[x] = ++num;
    if (son[x] > 0) dfs2(son[x],tp);
    for (int i = 0;i < v[x].size(); i++)
        {
            int nxt = v[x][i];
            if (nxt == fa[x] || nxt == son[x]) continue;
            dfs2(nxt,nxt);
        }
}
void build(int l,int r,int rt)
{
    if (l == r) {tree[rt].val = val[l]; return;}
    int mid = (l + r) >> 1;
    build(l,mid,rt << 1);
    build(mid+1,r,rt << 1 | 1);
    tree[rt].val = tree[rt<<1].val+tree[rt<<1|1].val;
}
int query(int rt,int l,int r,int L,int R)
{
    if (L <= l && r <= R) return tree[rt].val;
    int mid = (l + r) >> 1;
    int res = 0;
    if (L <= mid) res += query(rt<<1,l,mid,L,R);
    if (R > mid) res += query(rt<<1|1,mid+1,r,L,R);
    return res;
}
void link(int u,int v,int w)
{
    int top1 = top[u]; int top2 = top[v];
    while (top1 != top2)
        {
            if (dep[top1] < dep[top2])
                {swap(top1,top2); swap(u,v);}
            if (w == 1) res += query(1,1,n,id[top1],id[u]);
            if (w == 2) {d[id[top1]]++; d[id[u]+1]--;}
            u = fa[top1];
            top1 = top[u];
        }
    if (u == v) return;
    if (dep[u] > dep[v]) swap(u,v);
    if (w == 1) res += query(1,1,n,id[son[u]],id[v]);
    if (w == 2) {d[id[son[u]]]++; d[id[v]+1]--;}
}
bool judge(int x)
{
    int cnt=0,i,mx=0;
    for (i = 1;i < n; i++) d[i] = 0;
    for (i = 1;i <= qq; i++)
        if (q[i].rate > x)
            {cnt++; link(q[i].x,q[i].y,2); mx=max(mx,q[i].rate);}
    for (i = 1;i < n; i++) a[i] = a[i - 1] + d[i];
    for (i = 1;i < n; i++)
        if (a[i] == cnt)
            if (mx - val[i] <= x) return true;
    return false;
}
int main()
{
    scanf("%d%d",&n,&qq);
    for (i = 1;i < n; i++)
        {
            e[i].x = read(); e[i].y = read(); e[i].val = read();
            v[e[i].x].push_back(e[i].y);
            v[e[i].y].push_back(e[i].x);
        }

    num = 0; dfs1(1,0,1); dfs2(1,1);
    for (i = 1;i <= n; i++)
        {
            if (dep[e[i].x] < dep[e[i].y]) swap(e[i].x,e[i].y);
            val[id[e[i].x]] = e[i].val;
        }
    build(1,n,1); l = r = 0;
    for (i = 1;i <= qq; i++)
        {
            q[i].x = read(); q[i].y = read();
            res = 0; link(q[i].x,q[i].y,1); q[i].rate = res;
            r = max(r,res);
        }
    //sort(q+1,q+qq+1,cmp);
    while (l <= r)
        {
            mid = (l + r) >> 1;
            if (judge(mid)) {r = mid - 1; res = mid;} else l = mid + 1;
        }
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值