Codeforces 671D. Roads in Yusland(树形DP+线段树)

  调了半天居然还能是线段树写错了,药丸

  这题大概是类似一个树形DP的东西。设$dp[i]$为修完i这棵子树的最小代价,假设当前点为$x$,但是转移的时候我们不知道子节点到底有没有一条越过$x$的路。如果我们枚举每条路去转移,会发现这条路沿线上的其他子树的答案难以统计,那怎么办呢,我们可以让这条路向上回溯的时候顺便记录一下,于是有$val[i]$表示必修i这条路,并且修完当前子树的最小代价。

  则有转移$dp[x]=min(val[j])$,且$j$这条路必须覆盖$x$。

  $val[i]=(\sum dp[son])-dp[sonx]+val[i]$,且$i$这条路必须覆盖$sonx$。

  转移用线段树来维护就好,至于怎么判断某条路是否覆盖两个点,只要递归到某条路的起点的时候把$val[i]$改为$(\sum dp[son])+cost[i]$,递归到某条路终点的时候把$val[i]$改为$inf$就好了。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=500010;
const ll inf=1e15;
struct poi{ll sum, delta;}tree[maxn<<2];
struct tjm{int too, pre;}e[maxn<<1], e2[maxn<<1], e3[maxn<<1];
struct qaq{int x, y, cost, pos;}q[maxn];
ll dp[maxn];
int n, m, x, y, tot, tot2, tot3, tott, l[maxn], r[maxn], last[maxn], last2[maxn], last3[maxn];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<‘0‘ || c>‘9‘) c==‘-‘ && (f=-1), c=getchar();
    while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar();
    k*=f;
}
inline void add(int x, int y){e[++tot]=(tjm){y, last[x]}; last[x]=tot;}
inline void add2(int x, int y){e2[++tot2]=(tjm){y, last2[x]}; last2[x]=tot2;}
inline void add3(int x, int y){e3[++tot3]=(tjm){y, last3[x]}; last3[x]=tot3;}
inline void up(int x) {tree[x].sum=min(tree[x<<1].sum, tree[x<<1|1].sum);}
inline void addone(int x, int l, int r, ll delta)
{
    tree[x].delta=min(inf, tree[x].delta+delta);
    tree[x].sum=min(inf, tree[x].sum+delta);
}
inline void down(int x, int l, int r)
{
    int mid=(l+r)>>1;
    addone(x<<1, l, mid, tree[x].delta);
    addone(x<<1|1, mid+1, r, tree[x].delta);
    tree[x].delta=0;
}
void build(int x, int l, int r)
{
    if(l==r) {tree[x].sum=inf; return;}
    int mid=(l+r)>>1;
    build(x<<1, l, mid); build(x<<1|1, mid+1, r);
    up(x);
}
void update(int x, int l, int r, int cx, ll delta)
{
    if(l==r) {tree[x].sum=delta; return;}
    down(x, l, r);
    int mid=(l+r)>>1;
    if(cx<=mid) update(x<<1, l, mid, cx, delta);
    else update(x<<1|1, mid+1, r, cx, delta);
    up(x);
}
void change(int x, int l, int r, int cl, int cr, ll delta)
{
    if(cl>cr) return;
    if(cl<=l && r<=cr) {addone(x, l, r, delta); return;}
    down(x, l, r);
    int mid=(l+r)>>1;
    if(cl<=mid) change(x<<1, l, mid, cl, cr, delta);
    if(cr>mid) change(x<<1|1, mid+1, r, cl, cr, delta);
    up(x);
}
ll query(int x, int l, int r, int cl, int cr)
{
    if(cl>cr) return inf;
    if(cl<=l && r<=cr) return tree[x].sum;
    down(x, l, r);
    int mid=(l+r)>>1; ll ans=inf;
    if(cl<=mid) ans=query(x<<1, l, mid, cl, cr);
    if(cr>mid) ans=min(ans, query(x<<1|1, mid+1, r, cl, cr));
    return ans;
}
void dfs1(int x, int fa)
{
    l[x]=++tott;
    for(int i=last[x], too;i;i=e[i].pre)
    if((too=e[i].too)!=fa) dfs1(too, x);
    r[x]=tott;
}
inline int find(int x)
{
    int l=1, r=m+1;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(q[mid].pos>=x) r=mid;
        else l=mid+1;
    }
    return l;
}
void dfs2(int x, int fa)
{
    ll sum=0;
    for(int i=last[x], too;i;i=e[i].pre)
    if((too=e[i].too)!=fa) dfs2(too, x), sum=min(inf, sum+dp[too]);
    if(x==1) {dp[1]=sum; return;}
    for(int i=last2[x];i;i=e2[i].pre) update(1, 1, m, e2[i].too, min(inf, q[e2[i].too].cost+sum));
    for(int i=last3[x];i;i=e3[i].pre) update(1, 1, m, e3[i].too, inf);
    for(int i=last[x], too;i;i=e[i].pre)
    if((too=e[i].too)!=fa) change(1, 1, m, find(l[too]), find(r[too]+1)-1, sum-dp[too]);
    dp[x]=query(1, 1, m, find(l[x]), find(r[x]+1)-1);
}
inline bool cmp(qaq a, qaq b){return a.pos<b.pos;}
int main()
{
    read(n); read(m); build(1, 1, m);
    for(int i=1;i<n;i++) read(x), read(y), add(x, y), add(y, x);
    dfs1(1, 0);
    for(int i=1;i<=m;i++) read(q[i].x), read(q[i].y), read(q[i].cost), q[i].pos=l[q[i].x];
    sort(q+1, q+1+m, cmp); q[m+1].pos=n+1;
    for(int i=1;i<=m;i++) add2(q[i].x, i), add3(q[i].y, i);
    dfs2(1, 0);
    if(dp[1]>=inf) return puts("-1"), 0;
    printf("%lld\n", dp[1]);
}

时间: 2024-11-10 16:08:18

Codeforces 671D. Roads in Yusland(树形DP+线段树)的相关文章

hdu5293 Tree chain problem 树形dp+线段树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5293 在一棵树中,给出若干条链和链的权值.求选取不相交的链使得权值和最大. 比赛的时候以为是树链剖分就果断没去想,事实上是没思路. 看了题解,原来是树形dp.话说多校第一场树形dp还真多. . .. 维护d[i],表示以i为根节点的子树的最优答案. sum[i]表示i的儿子节点(仅仅能是儿子节点)的d值和. 那么答案就是d[root]. 怎样更新d值 d[i] = max(sum[i] , w[p]+s

codeforces 340D D. Bubble Sort Graph(dp+线段树)

题目链接: codeforces 340D 题目大意: 给出一个程序,就是冒泡排序,每次如果发现相邻两个数前一个大于后一个,交换位置后建边,问最后得到的这个图中的最大独立集,这个最大独立集定义为所有的点都不相邻的最大点的集合的规模. 题目分析: 首先我们可以知道对于a[i],它只会且一定会与后面的比它小的建边,所以我们只需要固定第一个点,然后找最长上升子序列即可.(这样能够保证没有相邻的点) 定义状态dp[i]为以i项结尾的最长上升子序列的长度. 转移方程如下: dp[i]=max{dp[j]+

POJ 3162 Walking Race 树形DP+线段树

给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成为一段长度为n的数列 现在mm想要从这数列中选出一个连续的区间,要求这个区间的max-min<=m 输出最长的区间 做了一个下午 思路: 分成2个部分: 1.求出数列,即对于一棵树,求出每一个节点能到达的最远距离 2.对于这段数列,选出一个区间,使得区间的max-min<=m,并且使得区间长度尽量

poj3162(树形dp+线段树求最大最小值)

题目链接:https://vjudge.net/problem/POJ-3162 题意:给一棵树,求每个结点的树上最远距离,记为a[i],然后求最大区间[l,r]满足区间内的max(a[i])-min(a[i])<=M. 思路:第一步向hdoj2196那题一样树形dp求出每个结点的最长距离,我的另一篇博客中有写到https://www.cnblogs.com/FrankChen831X/p/11375572.html.求出最远距离a[i]后,建立线段树维护区间的最大最小值.然后用两个指针i,j遍

Codeforces Round #530 (Div. 2)F Cookies (树形dp+线段树)

题:https://codeforces.com/contest/1099/problem/F 题意:给定一个树,每个节点有俩个信息x和t,分别表示这个节点上的饼干个数和先手吃掉这个节点上一个饼干的的时间.然后有先手和后手俩个人. ?先手可以这么操作:在规定总时间T到达某个节点然后一定要返回根节点1,期间可以选择吃掉某些节点上的某些饼干(前提是保证剩下的时间能够回到根节点): ?后手可以这么操作:在先手到达的位置和这个位置的孩子之间的连边选择一条让先手吃得更多的边摧毁掉,也可以跳过这个过程: 问

Codeforces Round #530 (Div. 2) E (树形dp+线段树)

链接: 题意: 给你一棵树,树上有n个节点,每个节点上有ai块饼干,在这个节点上的每块饼干需要花费bi时间,有两个玩家,玩家一可以移动到当前点的子节点也可以申请游戏结束返回根节点并吃沿途的饼干,玩家二可以删除当前点到儿子节点的一条边,走路和吃饼干都消耗时间,会给出一个总时间,在总时间内尽可能的多吃饼干,问最多能吃多少个? 思路: 由于是玩家一先手,那么最开始的最大边则不会被删除,但之后路途的最大边都会被玩家二删除,所以我们对于当前点我们需要求: 1.如果现在回头那么最多可以吃到多少饼干 2.向下

CodeForces 52C Circular RMQ(区间循环线段树,区间更新,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://codeforces.com/problemset/problem/52/C You are given circular array a0,?a1,?...,?an?-?1. There are two types of operations with it: inc(lf,?rg,?v) - this operation increases each element on the segm

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

树形DP求树的重心 --SGU 134

令一个点的属性值为:去除这个点以及与这个点相连的所有边后得到的连通分量的节点数的最大值. 则树的重心定义为:一个点,这个点的属性值在所有点中是最小的. SGU 134 即要找出所有的重心,并且找出重心的属性值. 考虑用树形DP. dp[u]表示割去u点,得到的连通分支的节点数的最大值. tot[u]记录以u为根的这棵子树的节点数总和(包括根). 则用一次dfs即可预处理出这两个数组.再枚举每个点,每个点的属性值其实为max(dp[u],n-tot[u]),因为有可能最大的连通分支在u的父亲及以上