竞赛题解 - Broken Tree(CF-758E)

Broken Tree(CF-758E) - 竞赛题解

贪心复习~(好像暴露了什么算法……)
标签:贪心 / DFS


『题意』

给出一棵以1为根的树,每条边有两个值:p-强度、w-重量。
对于给出的树,我们可以对每条边进行操作——将它的p、w同时减去相同的值,但是要求 \(p\ge0,w>0\) 。(注意只能减,不能加
进行操作后,需要使原树满足:如果 u 是 v 的父亲,那么 u 到 v 的边的 p 不能小于 以 v 为根节点的子树中所有边的 w 之和。
求出一种方案,使得在满足条件的情况下,树的 w 之和最大,输出这个方案。如果不存在任何方案,输出-1(Of course 有 SPJ)


『解析』

简单地思考一下,对于这道题,叶子节点的p以及与根节点相连的边的w是没有用的……
根据这个我们可以想出一个思路——从下到上尽可能减去重量,求到重量最小的树;再从上到下贪心地尽可能给每条边加重量(不超过原重量),得到答案。比较有趣的是正解好像也是这么写的——因为按理来说样例的解很多,但是我的程序跑出来是一样的~

「减重量-DFS」

从根节点DFS到叶子节点,然后从叶子节点递归得到整个树的最小重量解(当然是唯一解)
声明一下变量:

(1) edg[i] 按照输入顺序给出的第i条边(从1开始);

(2) fedg[i] 对应edg[i],表示修改后的第i条边;

(3) pnt[i] 表示以i为根的子树的最小总重量(满足条件的);

(4) (u,v) 表示 u,v 之间的边在输入时的顺序
Tab. edg,fedg 都是结构体,包含元素 wgt,ref 分别表示重量、强度

假设现在是 u点,已经计算出它的儿子 v,边的编号 id=(u,v)。
首先判断重量是否合法——如果子树v的最小重量 pnt[v] 都大于 edg[id].ref 了,那么就不合法,输出-1。
否则,显然如果不考虑 \(edg[id].wgt>0\) ,那么我们可以把 edg[id].ref 降至 pnt[v] —— 那么我们就是要在 \(edg[id].wgt>0\) 的情况下尽可能的使 edg[id].ref 小。计算 \(delta\) 表示将 edg[id] 的 wgt 和 ref 同时减去 delta。那么 \(delta=min\{edg[id].ref-hvy\ ,\ edg[id].wgt-1\}\),将削减后的值存入 fedg[id] ,最后统计 pnt[u] 。

这样我们就求出了最小重量的方案(同时判断了不存在解的情况)。

「增重量-Solve」

根据最开始的分析,我们可以将与根节点相连的所有边的 wgt 和 ref 都调至最大(也就是初始值)。
在 Solve() 函数中除了 当前节点u 还附带一个变量 \(del\)表示u的子树在最小基础下的能够增加的最大重量。其实Solve()的本质还是一个 DFS……但是它的返回值是 以u为根的子树中在最小重量的基础上新增加的重量之和,所以我们用deltot来记录这个返回值。

那么从根节点出发,我们可以把 del 看做是正无穷,因为没有任何限制。
假设现在正在处理 边(u,v) ,u是父亲。
如果当前边再加上 del 不超过原来的重量,那么就可以将它加上,返回 deltot+=del(这里del就相当于加重量的机会,而这种情况就相当于把所有机会用完了)。
否则在给当前边增加重量后,del还有剩余——那么就贪心地把 del 分配到 v 里去。也就是先给 边(u,v) 加大到可能的最大值,同时将 del 减去增加的值。但是我们不能直接将 del 下传到 v 去,因为可能 v 的子树在增加 del 的重量后,边(u,v) 的强度不够,所以下传时,我们进行一个处理—— \(min(del,fedg[id].ref-pnt[v])\)(这里的pnt[v]其实就是在未对v进行 Solve() 修改时的v的子树总重)。最后在 Solve() 返回时将 del 减去其返回值,表示用去了这么多“机会”。

然后就可以了~


『源代码』

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=(int)2e5;
struct GRAPH{
    struct NODE{
        int to,nxt,id;
        NODE(){}
        NODE(int _to,int _nxt,int _id):to(_to),nxt(_nxt),id(_id){}
    }nod[N*2+7];
    int adj[N+7],cnt,siz[N+7];
    GRAPH(){memset(adj,-1,sizeof adj);cnt=0;}
    void AddEdge(int u,int v,int id,bool dir){
        siz[u]++;nod[++cnt]=NODE(v,adj[u],id);adj[u]=cnt;
        if(!dir) AddEdge(v,u,id,true);
    }
}grp;
struct EDGE{
    int u,v;
    long long ref,wgt;
}edg[N+7],fedg[N+7];
int n;
long long pnt[N+7];
long long DFS(int u,int pre){
    long long hvytot=0;
    for(int i=grp.adj[u];i!=-1;i=grp.nod[i].nxt){
        int v=grp.nod[i].to,id=grp.nod[i].id;
        if(v==pre) continue;
        long long hvy=DFS(v,u);
        if(hvy>edg[id].ref){
            printf("-1\n");
            exit(0);
        }
        long long delta=min(edg[id].ref-hvy,edg[id].wgt-1);
        fedg[id].ref=edg[id].ref-delta;
        fedg[id].wgt=edg[id].wgt-delta;
        hvytot+=hvy+fedg[id].wgt;
    }
    return pnt[u]=hvytot;
}
long long Solve(int u,int pre,long long del){ //del:u的子树在最小基础下的能够增加的最大重量
    long long deltot=0;
    for(int i=grp.adj[u];i!=-1;i=grp.nod[i].nxt){
        int v=grp.nod[i].to,id=grp.nod[i].id;
        if(v==pre) continue;
        if(fedg[id].wgt+del<=edg[id].wgt){
            fedg[id].wgt+=del;fedg[id].ref+=del;
            deltot+=del;
            del=0;break;
        }
        else{
            deltot+=edg[id].wgt-fedg[id].wgt;
            del-=edg[id].wgt-fedg[id].wgt;
            fedg[id]=edg[id];
        }
        long long delv=Solve(v,u,min(del,fedg[id].ref-pnt[v]));
        del-=delv;deltot+=delv;
    }
    return deltot;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d%d%d",&edg[i].u,&edg[i].v,&edg[i].wgt,&edg[i].ref);
        fedg[i]=edg[i];
        grp.AddEdge(edg[i].u,edg[i].v,i,false);
    }
    DFS(1,0);
    Solve(1,0,(1ll<<60));
    printf("%d\n",n);
    for(int i=1;i<n;i++)
        printf("%d %d %lld %lld\n",fedg[i].u,fedg[i].v,fedg[i].wgt,fedg[i].ref);
    return 0;
}

\(\mathcal{The\ End}\)

\(\mathcal{Thanks\ For\ Reading!}\)

- 如果blog里面有没讲清楚的……可以在我的邮箱\(lucky\[email protected]\)问我~

原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/10160577.html

时间: 2024-08-01 12:49:56

竞赛题解 - Broken Tree(CF-758E)的相关文章

[LeetCode 题解]: Binary Tree Preorder Traversal

Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tree {1,#,2,3}, 1 2 / 3 return [1,2,3]. Note: Recursive solution is trivial, could you do it iteratively? 题意 先序遍历二叉树,递归的思路是普通的,能否用迭代呢? 非递归思路:<借助stack>

[LeetCode 题解]: Symmetric Tree

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For example, this binary tree is symmetric: 1 / 2 2 / \ / 3 4 4 3 But the following is not: 1 / 2 2 \ 3 3 Note:Bonus points if you could solve it both recu

leetcode题解:Binary Tree Postorder Traversal (二叉树的后序遍历)

题目: Given a binary tree, return the postorder traversal of its nodes' values. For example:Given binary tree {1,#,2,3}, 1 2 / 3 return [3,2,1]. Note: Recursive solution is trivial, could you do it iteratively? 说明: 1) 两种实现,递归与非递归 , 其中非递归有两种方法 2)复杂度分析:时

《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就是一本题解书,简单暴力的通过题目的堆叠来提升解决编程问题的能力. 那么下面开始探索吧. zoj1037: BackgroundFor years, computer scientists have been trying to find efficient solutions to different computing p

leetcode题解:Tree Level Order Traversal II (二叉树的层序遍历 2)

题目: Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). For example:Given binary tree {3,9,20,#,#,15,7}, 3 / 9 20 / 15 7 return its bottom-up level order tr

leetcode 题解:Binary Tree Level Order Traversal (二叉树的层序遍历)

题目: Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). For example:Given binary tree {3,9,20,#,#,15,7}, 3 / 9 20 / 15 7 return its level order traversal as: [ [3], [9,20], [15,7] ] co

【codeforces】【比赛题解】#854 CF Round #433 (Div.2)

cf一如既往挺丧 看丧题点我! [A]分数 Petya是数学迷,特别是有关于分数的数学.最近他学了所谓一个分数被叫做"真分数"当且仅当其分子小于分母,而一个分数被叫做"最简分数"当且仅当其分子分母互质.在闲暇时间,Petya在用计算器研究:如何把最简真分数转换为小数等问题.有一天他不小心把除号(÷)按成了加号(+),导致他得到了分子与分母的和.Petya想要得到他原来的分数,但他很快发现这不是唯一的.所以现在他想要知道最大的最简真分数使得其分子与分母的和为n. 输入

【codeforces】【比赛题解】#849 CF Round #431 (Div.2)

cf的比赛越来越有难度了--至少我做起来是这样. 先看看题目吧:点我. 这次比赛是北京时间21:35开始的,算是比较良心. [A]奇数与结束 "奇数从哪里开始,又在哪里结束?梦想从何处起航,它们又是否会破灭呢?" 给定一个长度为n的序列.确定能不能将序列分成奇数个长度为奇数的非空字串,而且这其中每个子串以奇数开头,以奇数结尾.可以只分成一个(1也是奇数). 输入 第一行一个正整数n,表示序列长度. 第二行n个整数,表示序列中的元素. 输出 输出"Yes"或"

LeetCode(100)题解--Same Tree

https://leetcode.com/problems/same-tree/ 题目: Given two binary trees, write a function to check if they are equal or not. Two binary trees are considered equal if they are structurally identical and the nodes have the same value. 思路:  DFS AC代码: 1.递归 1