zoukankan      html  css  js  c++  java
  • 【uoj150】 NOIP2015—运输计划

    http://uoj.ac/problem/150 (题目链接)

    题意

      给出一棵树以及m个询问,可以将树上一条边的权值修改为0,求经过这样的修改之后最长的边最短是多少。

    Solution 

      老早就听说过这道题了,好像使用树链剖分。 

      先树链剖分求出每个询问的路程,最长的最短,可以用二分做。二分最长的边的大小,也就是最后的答案,问题来了,怎么判断这个答案是否可行呢? 

      我们记录下所有超出当前答案的询问的个数p,用d记录下符合条件的边比当前二分的答案最大大多少,并给所有询问的两端点u,v的sum[]加上1,给他们的最近公共祖先f的sum[]减去2。这样做有什么用呢?这样就可以统计每条边经过了多少次了。 

      我们通过dfs,每经过一条边i,就把cnts[i]加上当前节点的sum值,这就代表有多少个点会经过这条边,回溯的时候更新sum即可。 

      最后的时候如果存在一条边被经过的次数正好等于当前询问数p,并且这条边的长度大于等于d,那么就是合法的,否则不合法。 

      其实这样的话根本就不用写树链剖分,dfs一遍就可以记录两点间距离了。。。然而树链剖分版不知道为什么最后uoj上extra test被卡的爆空间了。。好像是爆栈,于是手动开无限栈,MLE?!而且读入优化也gi了,真的鬼畜。。。无奈最后换成dfs版,没想到TLE。。。为什么bzoj上就能AC捏。

    细节

      差分的时候计数器数组cnts开成边数的空间。

    代码

    // uoj150
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define pragma comment(linker,"/STACK:1024000000,1024000000")
    #define LL long long
    #define inf 2147483640
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    int getint() {
        int f=1,x=0;char ch=getchar();
        while (ch<='0' || ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    const int maxn=300010;
    struct edge {int w,to,next;}e[maxn<<1];
    struct ask {int u,v,dis;}q[maxn];
    int bin[30],fa[maxn][30],head[maxn],deep[maxn],sum[maxn],d[maxn],cnts[maxn<<1];
    int n,m,cnt,num;
     
    void link(int u,int v,int w) {
        e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w;
        e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].w=w;
    }
    void dfs1(int x) {
    	for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    	for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
    			d[e[i].to]=d[x]+e[i].w;
    			deep[e[i].to]=deep[x]+1;
    			fa[e[i].to][0]=x;
    			dfs1(e[i].to);
    		}
    }
    int lca(int x,int y) {
        if (deep[x]<deep[y]) swap(x,y);
        int t=deep[x]-deep[y];
        for (int i=0;bin[i]<=t;i++) if (t&bin[i]) x=fa[x][i];
        for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
        return x==y?x:fa[x][0];
    }
    void dfs(int x) {
        for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
                dfs(e[i].to);
                sum[x]+=sum[e[i].to];
                cnts[i]=sum[e[i].to];
            }
    }
    bool check(int mid) {
        int d=0,p=0;
        memset(sum,0,sizeof(sum));
        for (int u=q[1].u,v=q[1].v,i=1;i<=n;i++,u=q[i].u,v=q[i].v)
            if (q[i].dis>mid) {
                sum[u]++;sum[v]++;
                sum[lca(u,v)]-=2;
                p++;
                d=max(d,q[i].dis-mid);
            }
        dfs(1);
        for (int i=1;i<=cnt;i++) if (p==cnts[i] && e[i].w>=d) return 1;
        return 0;
    }
    int main() {
        bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
        scanf("%d%d",&n,&m);
        for (int u,v,w,i=1;i<n;i++) {
            scanf("%d%d%d",&u,&v,&w);
            link(u,v,w);
        }
    	dfs1(1);
        int L=0,R=-inf,ans=0;
        for (int u,v,i=1;i<=m;i++) {
            q[i].u=u=getint(),q[i].v=v=getint();
            int f=lca(u,v);
            q[i].dis=d[u]+d[v]-2*d[f];
            R=max(q[i].dis,R);
        }
        while (L<=R) {
            int mid=(L+R)>>1;
            if (check(mid)) {ans=mid;R=mid-1;}
            else L=mid+1;
        }
        printf("%d",ans);
        return 0;
    }
    

      

      

  • 相关阅读:
    layui学习
    网络安装Centos x64 6.10
    给没有连接因特网的centos使用yum安装其他软件。
    Java使用线程池
    记录一次查看后台是否在运行资源备份上报到华为云存储的过程
    潭州课堂python
    什么是动态规划?动态规划的意义是什么?
    南明区教师信息管理系统之审批流程设计思路
    连接慢的主要原因是DNS解析导致
    jfinal中,render的时候如何取到view根目录
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/5914097.html
Copyright © 2011-2022 走看看