NOIP2015提高组——运输计划(transport)

来源:互联网 发布:苹果mac如何装虚拟机 编辑:程序博客网 时间:2024/06/11 03:24

题目链接

题意:给定一棵有n个节点的树(带有边权),再给定m条树中的路径。允许将一条树边的权改为0,并使得这些路径的最长距离最小。输出该修改方案中这些路径中最长的一条。

(n,m<=300000)

分析:

要求最大值最小,首先想到的是二分答案,即二分最终这些路径的长度中最长的一条,设为k,然后再判断是否可以做到。那么该如何判断可行性呢?

如果某一路径的距离大于k,那么显然需要修改的边是这条路径中的某条边。那么,就可以先将这些路径标记起来,然后要找出一条这些路径都覆盖边,如果有某一条边能满足,即该边的权值>=最长路径-答案k,那么这个方案是可行的。

还有一个问题,怎样找出这些边?如果树退化成一条链,其实就是求某点有多少个区间覆盖,用差分即可。那么,树上差分!

对于所有的路径i,设两头分别为a[i]与b[i],l[i]=LCA(s[i],t[i]),则预处理时f[a[i]]++,f[b[i]++],f[l[i]]-=2;按dfs序的倒序来做,每次将自己的f[i]加到父亲的f[i],这样就可以统计了。

最后一个问题,数据比较大,一定要注意常数优化(下面是建议):

1,将用vector存的邻接表改成链式前向星,

2,输入优化(getchar)

3,可以使用Tarjan算法来求LCA

4,二分的时候可将初始范围尽量缩小。


【程序(使用的是倍增算法求LCA)(时间复杂度O(nlogn))】

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#include <cstdlib>#include <cmath>#include <vector>#include <queue>using namespace std;#define N 300010#define logN 24#define INF 0x3fffffffvoid read(int &t){    char ch;    while(ch = getchar())        if(ch>='0' && ch<='9')            break;        t = ch - '0';    while(ch = getchar())        if(ch>='0' && ch<='9')            t = (t<<3)+(t<<1)+ch-'0';        else break;}struct Edge{    int to;    int val;    int next;};Edge edge[N<<1];int head[N];int cnt;void AddEdge(int u,int v,int val){    edge[cnt].val = val;    edge[cnt].to = v;    edge[cnt].next = head[u];    head[u] = cnt++;}int p[N],dis[N],dep[N],gra[N][logN],topre[N];void dfs(int u,int pre){    p[++cnt] = u;        for(int i=1;i<logN;i++)    {    gra[u][i] = gra[gra[u][i-1]][i-1];    if(!gra[u][i]) break;    }        for(int i = head[u];~i;i = edge[i].next)    {        int v = edge[i].to;        if(pre == v) continue;                dis[v] = dis[u] + edge[i].val;        dep[v] = dep[u] + 1;        gra[v][0] = u;                topre[v] = edge[i].val;                dfs(v,u);    }}int LCA(int x,int y){    if(dep[x]>dep[y]) swap(x,y);    for(int i = logN-1;i>=0;i--)        if(dep[gra[y][i]] >= dep[x])            y = gra[y][i];    for(int i = logN-1;i>=0;i--)        if(gra[y][i] != gra[x][i])        {            x = gra[x][i]; y = gra[y][i];        }    if(x != y) x = gra[x][0];    return x;}int n,m,Maxx,Minn;int f[N],a[N],b[N],l[N],d[N];bool check(int k){    memset(f,0,sizeof(f));    cnt = 0;        for(int i=1;i<=m;i++)     if(d[i] > k)    {        f[a[i]]++; f[b[i]]++; f[l[i]] -= 2;        cnt++;    }        for(int i=n;i>=1;i--)    {        f[gra[p[i]][0]] += f[p[i]];        if(topre[p[i]] >= Maxx - k && f[p[i]] == cnt) return true;    }    return false;}int Binary_Search(int l,int r){    int mid;        while(l < r)    {        mid = (l+r)>>1;                if(check(mid)) r = mid;        else l = mid+1;    }    return l;}int main(){    read(n); read(m);        memset(head,-1,sizeof(head));        int x,y,z,Max1;        Max1 = 0;    for(int i=1;i<n;i++)    {        read(x); read(y); read(z);        AddEdge(x,y,z);        AddEdge(y,x,z);        Max1 = max(Max1,z);    }        cnt = 0; dep[1] = 1; dep[0] = -1;    dfs(1,0);        for(int i=1;i<=m;i++)    {        read(a[i]); read(b[i]);        l[i] = LCA(a[i],b[i]);        d[i] = dis[a[i]] + dis[b[i]] - (dis[l[i]] << 1);        Maxx = max(Maxx,d[i]);    }        printf("%d\n",Binary_Search(Maxx - Max1,Maxx+1));        return 0;}


原创粉丝点击