zoukankan      html  css  js  c++  java
  • bzoj3252

    3252: 攻略

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 469  Solved: 194
    [Submit][Status][Discuss]

    Description

    题目简述:树版[k取方格数]
     
    众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。
    今天他得到了一款新游戏《XX半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)
    “为什么你还没玩就知道每个场景的价值呢?”
    “我已经看到结局了。”

    Input

    第一行两个正整数n,k
    第二行n个正整数,表示每个场景的价值
    以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)
    保证场景1为根节点

    Output

     
    输出一个整数表示答案

    Sample Input

    5 2
    4 3 2 1 1
    1 2
    1 5
    2 3
    2 4

    Sample Output

    10

    HINT

    对于100%的数据,n<=200000,1<=场景价值<=2^31-1

    Source

    我太zz了,竟然没想出来。。。
    一直觉得一个一个爬上去太慢了,看了题解发现自己是个zz,一共只删n个,也就是说只用爬n次就行了
    那么dfs序+线段树 每次给子树全部减这个点的值 然后把这个点的值清0 用线段树维护最大值和位置。
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<ll, int> PII;
    const int N = 200010;
    struct edge {
        int nxt, to;
    } e[N << 1];
    int n, k, cnt = 1, tot;
    ll ans;
    int head[N], dfn[N], low[N], fa[N], mp[N];
    ll v[N], w[N], tag[N << 2];
    PII tree[N << 2];
    void link(int u, int v)
    {
        e[++cnt].nxt = head[u];
        head[u] = cnt;
        e[cnt].to = v;
    }
    namespace seg
    {
        void pushdown(int x)
        {
            if(!tag[x]) return;
            tree[x << 1].first += tag[x]; tree[x << 1 | 1].first += tag[x];
            tag[x << 1] += tag[x]; tag[x << 1 | 1] += tag[x];
            tag[x] = 0;         
        }
        void build(int l, int r, int x)
        {
            if(l == r) { tree[x].first = w[mp[l]]; tree[x].second = mp[l]; return; }
            int mid = (l + r) >> 1;
            build(l, mid, x << 1); build(mid + 1, r, x << 1 | 1);
            if(tree[x << 1].first > tree[x << 1 | 1].first) tree[x] = tree[x << 1];
            else tree[x] = tree[x << 1 | 1];
        }
        void update(int l, int r, int x, int a, int b, int num)
        {
            if(l > b || r < a) return;
            if(l >= a && r <= b)
            {
                tree[x].first += num; tag[x] += num; return; 
            }
            pushdown(x);
            int mid = (l + r) >> 1;
            update(l, mid, x << 1, a, b, num);
            update(mid + 1, r, x << 1 | 1, a, b, num);
            if(tree[x << 1].first > tree[x << 1 | 1].first) tree[x] = tree[x << 1];
            else tree[x] = tree[x << 1 | 1]; 
        }
    } using namespace seg;
    void dfs(int u, int last, ll sum)
    {
        dfn[u] = ++tot; mp[tot] = u; w[u] = sum;
        for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != last)
        {
            fa[e[i].to] = u;
            dfs(e[i].to, u, sum + v[e[i].to]);
        }
        low[u] = tot;
    }
    void change(int pos)
    {
        while(v[pos])
        {
            update(1, n, 1, dfn[pos], low[pos], -v[pos]);
            v[pos] = 0; pos = fa[pos];
        }
    }
    int main()
    {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++i) scanf("%lld", &v[i]);
        for(int i = 1; i < n; ++i)
        {
            int u, v; scanf("%d%d", &u, &v);
            link(u, v); link(v, u);
        }
        ans = v[1];
        v[1] = 0;
        dfs(1, 0, 0);
        build(1, n, 1);
        while(k--)
        {
            PII x = tree[1];
            ans += x.first;
            change(x.second);    
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    华师菜鸟杯2020
    「算法」排序
    生成函数
    多项式乘法逆
    多项式牛顿迭代
    「数学」快速幂
    「算法」贪心
    「组合数学」一:什么是组合数学
    「具体数学」三:整值函数
    「图论」树上问题
  • 原文地址:https://www.cnblogs.com/19992147orz/p/6731251.html
Copyright © 2011-2022 走看看