• P3525 INS-Inspection


    这道题的题面有点问题,如果按照题面做,应该是A不了的,下面引用一下评论里@REM_001的翻译

    一棵n个节点的树,行动中心S从1->N。从S出发前往任意一个未标记到的点(沿树上两点的唯一路径走),标记该节点,然后返回S。相邻两次行动所经过的道路不允许有重复,最后一次标记后不需要返回,求路程总和的最小值。

    第i行输出行动中心为i时的答案,如果不可能则输出-1

    这种翻译好像跟题面没什么区别啊。

    别着急,下面就知道区别在哪里了。

    题面要求,相邻两次到达的点不能有公共边,说白了就是以 (S) 为根的树每次取的点不能来自同一颗 (S) 的子节点的子树。那么再简化一下,就是 (S) 的子节点的子树节点数最大不能超过(n/2)。为什么呢?如果超过一半了,肯定会出现相邻啊。

    那么这里就出现了一个临界值。(n-1)个点排成的队列,如果(n)是偶数,比如说是(4)。那么最大的子节点的子树大小最大就是(2)这个时候,队列的最后一个点就被确定一定是这个大小为(2)的子树上的点。意识到什么不对的地方了吗?

    如果按照题面翻译的那样,我们是不需要考虑这种情况的,但是正确的题面可是要考虑这个临界值的啊。这也就是有的人怎么改都A不了的原因了。题面都是错的,怎么可能写对嘛。

    上面我们讲了怎么判定,那么怎么求最大的子节点的子树大小呢?设 (s[u]) 表示以(u)为根的子树大小,我们先随便选一个点为根节点,然后dfs一遍求出(s[u]),此时的(s[u])表示的是以(u)的父节点为根节点时,以(u)为根的子树大小。当以(u)的子节点为根节点时,以(u)为根的子树大小为(n-s[u]+1),这里很好理解,就不说为什么了。

    能够判定了,我们还需要求距离和,还有最大距离。

    我们定义(w[u][0])表示以(u)为根节点的子树上的点到(u)的距离和,(w[u][1])表示不在以(u)为根节点的子树上的点到(u)的距离和,跑两遍dfs就可以求出。然后就是求距离,这个很简单,我们在下面的程序里说

    下放程序

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cctype>
    #define ll long long
    #define gc getchar
    #define maxn 1000005
    using namespace std;
    
    inline ll read(){
        ll a=0;int f=0;char p=gc();
        while(!isdigit(p)){f|=p=='-';p=gc();}
        while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
        return f?-a:a;
    }int n;ll ans;
    
    struct ahaha{
        int to,next;
    }e[maxn<<1];int tot,head[maxn];
    inline void add(int u,int v){
        e[tot].to=v,e[tot].next=head[u];head[u]=tot++;
    }
    
    int f[maxn],l[maxn],s[maxn];ll w[maxn][2];  //f记录父子关系,l记录最远距离,s记录子树大小,w[0]表示下方的点的距离和,w[1]表示上方的点的距离和
    int p[maxn],d1[maxn],d[maxn][2];  //p表示下方距离的最大值经过哪一个子节点,d1表示下方距离次大值,d[0]表示下方距离最大值,d[1]表示上方距离最大值
    void dfs(int u,int fa){s[u]=1;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;if(v==fa)continue;
            dfs(v,u);f[v]=u;s[u]+=s[v];  //先递归,再处理 s的处理也很常见
            w[u][0]+=w[v][0]+s[v];  //加上s[v]是加上uv相连的这条边的贡献
            if(d[v][0]+1<=d1[u])continue; //如果v下方最大值+1还不如u下方次大值大,我们就可以直接走了
            if(d[v][0]+1>=d[u][0]){  //如果大于等于最大值,更新次大值,更新最大值,更新最大值来源。为什么等于也可以大家可以自己想一想,很简单
                d1[u]=d[u][0];
                d[u][0]=d[v][0]+1,p[u]=v;
                continue;
            }
            d1[u]=d[v][0]+1;  //剩下的情况就是 d1[u]<d[v][0]+1<d[u][0] 所以更新次大值
        }
    }
    void dfs1(int u,int fa){
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;if(v==fa)continue;
            if(v==p[u])d[v][1]=max(d1[u],d[u][1])+1;  //如果是父节点的最大值来源,那么用次大值更新
            else d[v][1]=max(d[u][0],d[u][1])+1;  //反之,用最大值更新
            w[v][1]=(ll)n-s[v]+w[u][1]+w[u][0]-w[v][0]-s[v];  //n-s[v]表示uv相连的边的贡献,w[u][0]-w[v][0]-s[v]表示父节点的其他子节点的贡献
            dfs1(v,u);  //先处理,再递归
        }
    }
    
    int main(){memset(head,-1,sizeof head);
        n=read();
        if(n==1){  //对于只有一个节点的树我们直接特判
        	puts("0");
        	return 0;
        }	
        for(int i=1;i<n;++i){  //读入所有边
            int u=read(),v=read();
            add(u,v);add(v,u);
        }
        dfs(1,-1);dfs1(1,-1);  //第一遍dfs自下而上处理,第二遍自上而下
        for(int u=1;u<=n;++u){
            int maxa=0,a,k;
            for(int i=head[u];~i;i=e[i].next){  //求出以当前节点为根,最大的子节点的子树大小,并记录是哪个子节点
                int v=e[i].to;k=f[v]==u?s[v]:n-s[u];
                if(k>maxa)maxa=k,a=v;
            }
            if((maxa<<1)>n){  //如果超过一半,必定会相邻,输出-1
                puts("-1");
                continue;
            }
            l[u]=(maxa<<1)==n?(f[a]==u?d[a][0]+1:(max(p[a]==u?d1[a]:d[a][0],d[a][1])+1)):max(d[u][0],d[u][1]);  //这一行的意思是如果不是前面提到的临界值,那么直接找最大距离即可;但是如果是临界情况,并且这个子节点依然是子节点,那么就是子节点下面的最远距离+1,如果子节点之前是父节点,那么找子节点没有经过u的最长距离+1
            printf("%lld
    ",(w[u][0]+w[u][1]<<1)-l[u]); //最后输出答案即可
        }
        return 0;
    }
    

    这道题留给我们一个教训,就是不要过分相信翻译,毕竟谁都可以递交翻译

    看完不妨留个推荐?

  • 相关阅读:
    二、跳转指定页面的JS代码
    一、常规的JS页面跳转代码
    您在前一家公司的离职原因是什么?
    如果你在这次面试中没有被录用,你怎么打算?
    如果你在这次面试中没有被录用,你怎么打算?
    针对你们单位业务工作中出现的问题,你提出了一些很好的建议,得到了同事们的赞同,但你的领导并不满意。在这种情况下,你怎么办?
    进程与线程的区别
    work13
    work12
    day09
  • 原文地址:https://www.cnblogs.com/hanruyun/p/11422989.html
Copyright © 2020-2023  润新知