Flower【HDU-6849】【动态点分治+树状数组】

2020 Multi-University Training Contest 7 F题


  有一个N个点的树,给予其中M个操作,每次选其中一个点x,{x, r, v},给它一个影响范围为r的权值为v的值,我们现在想要选取最多的权值点,使得两两之间是没有可重叠区间的。

  这个问题画在一维平面上其实很好做,也就是对于一段排序,然后维护的就是一个线段树优化dp,当我们选取这个点pos的时候,我们只能选择dp[1 \rightarrow pos - r - 1]的点,或者说,我们假设在[(pos - r) \rightarrow (pos + r) ]中,如果选择其他的更优,那么,我们可以不将这个点加进我们所选的集合中去。

  于是,可以讲这个操作转换为,我们先将影响深度深的点给放进去,然后按照上面的类似贪心方法进行判断。

  如果我们取这个点,比之前选取的点更优的话,我们可以选择这个点代替和它冲突的点,当然,还是可以继承和它不冲突的点的,于是,我们首先对影响区间最深的点开始这样的操作,这样的目的是为了我们其实只用考虑一个端点,而且用一个类似于差分的思想,我们可以判断取这个点是否更优。

画图说明:

  如果存在这样一个点u,我们设它的深度为deep[u],然后对于u点假如存在一个影响的范围r,可以知道它最向上影响的深度为deep[u] - r的点,我们根据这个信息,来对所有的点进行{\color{Red} deep[u] - r}降序排序,于是,可以考虑之后放入的点v,如果被之前覆盖的点能够影响到的话,也就是对应它的向上r步的点(深度为deep[u] - r,或者当deep[u] - r为小于等于0时就是原树根节点),在点v的范围内,所以,我们将u的贡献点放在它向上r步的点,然后把它产生的贡献放在该点上。

  然后,现在就是查询有多少的冲突贡献,我们可以算它覆盖范围内的点的权值和即可,当然,这里的“点”指的是原点向上r步的点。

  贡献:假设冲突的点产生的总贡献为sum,那么这个点如果要取的话(如果它本身的价值还不足sum的话,要它何用?),那么它产生的贡献为v - sum。所以,对于放进去的第一个点,它的贡献就是v,因为在它之前就没有任何贡献了,所以根据这样的信息,我们可以维护出来所有点的关系,又因为它是根据“向上r步”的点来进行操作的,所以保证了影响点的深度一定是上升的,所以不存在先取之前的,而跳过中间的点的这样的情况。

操作实现

  现在问题转化了,变成了求它的r步范围内的“权值点”的权值之和,那样,我们可以用动态点分治来实现,因为按照类似时间戳这样的方式进行插入,所以不可永久化,我们用一个数据结构维护这个关系即可(自然选用简单的树状数组啦!)。

  所以,就是一个差分的关系了,我们找到这个点,然后不断的向它的点分治父亲节点走,每次算向周围r - dis(father, x)距离的点的权值和,当然,这样就很容易把自己方向的点给多算了,所以点分治的时候记录在自己方向上的father的下一个点即可。

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, Q;
namespace Graph
{
    int head[maxN], cnt;
    struct Eddge
    {
        int nex, to;
        Eddge(int a=-1, int b=0):nex(a), to(b) {}
    } edge[maxN << 1];
    inline void addEddge(int u, int v)
    {
        edge[cnt] = Eddge(head[u], v);
        head[u] = cnt++;
    }
    inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
    inline void init()
    {
        cnt = 0;
        for(int i=1; i<=N; i++) head[i] = -1;
    }
};
using namespace Graph;
namespace Segement
{
    vector<ll> tr[20][maxN];
    void Clear()
    {
        for(int i=0; i<20; i++)
        {
            for(int j=1; j<=N; j++)
            {
                tr[i][j].clear();
            }
        }
    }
    inline ll query(vector<ll> &vt, int x)  //x == distance + 1 in order to solve f(x == 0)
    {
        ll ans = 0;
        while(x)
        {
            ans += vt[x];
            x -= lowbit(x);
        }
        return ans;
    }
    inline void update(vector<ll> &vt, int x, ll val)
    {
        int _UP = (int)vt.size() - 1;
        while(x <= _UP)
        {
            vt[x] += val;
            x += lowbit(x);
        }
    }
};
using namespace Segement;
namespace Divide
{
    int vis[maxN];
    int maxx, root, all, fa[maxN], siz[maxN], son[maxN], dis_from_ff[maxN][20], under_ff[maxN][20];
    void Init()
    {
        for(int i=1; i<=N; i++) vis[i] = 0;
        maxx = INF;
    }
    void find_root(int u, int fa)
    {
        siz[u] = 1; son[u] = 0;
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(vis[v] || v == fa) continue;
            find_root(v, u);
            siz[u] += siz[v];
            son[u] = max(son[u], siz[v]);
        }
        son[u] = max(son[u], all - siz[u]);
        if(son[u] < maxx)
        {
            maxx = son[u];
            root = u;
        }
    }
    void dfs(int u, int father, int depth)
    {
        siz[u] = 1;
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(vis[v] || v == father) continue;
            dis_from_ff[v][depth] = dis_from_ff[u][depth] + 1;
            under_ff[v][depth] = under_ff[u][depth];
            dfs(v, u, depth);
            siz[u] += siz[v];
        }
    }
    void node_Divide(int u, int depth)
    {
        vis[u] = depth;
        tr[depth][u].resize(all + 2);
        int tot_siz = all;
        dis_from_ff[u][depth] = 0;
        for(int i=head[u], v, tmp_siz; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(vis[v]) continue;
            dis_from_ff[v][depth] = dis_from_ff[u][depth] + 1;
            under_ff[v][depth] = v;
            tmp_siz = siz[u] > siz[v] ? siz[v] : all - siz[u];
            tr[depth][v].resize(tmp_siz + 2);
            dfs(v, u, depth);
        }
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(vis[v]) continue;
            all = siz[u] > siz[v] ? siz[v] : tot_siz - siz[u];
            maxx = INF;
            find_root(v, u);
            fa[root] = u;
            dis_from_ff[root][depth + 1] = 0;
            under_ff[root][depth + 1] = root;
            node_Divide(root, depth + 1);
        }
    }
};
using namespace Divide;
int deep[maxN], lca_fa[maxN][20];
void dfs_Deep(int u, int father)
{
    deep[u] = deep[father] + 1; lca_fa[u][0] = father;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == father) continue;
        dfs_Deep(v, u);
    }
}
struct Question
{
    int x, r, v;
    Question(int a=0, int b=0, int c=0):x(a), r(b), v(c) {}
    inline void In() { scanf("%d%d%d", &x, &r, &v); }
    friend bool operator < (Question e1, Question e2) { return deep[e1.x] - e1.r > deep[e2.x] - e2.r; }
} ques[maxN];
inline ll ask(int x, int r)
{
    ll sum = 0;
    int depth = vis[x];
    sum = query(tr[depth][x], min(r + 1, (int)tr[depth][x].size() - 1));
    int y = fa[x];
    while(y)
    {
        depth--;
        if(dis_from_ff[x][depth] <= r)
        {
            sum += query(tr[depth][y], min(r + 1 - dis_from_ff[x][depth], (int)tr[depth][y].size() - 1));
            sum -= query(tr[depth][under_ff[x][depth]], min(r + 1 - dis_from_ff[x][depth], (int)tr[depth][under_ff[x][depth]].size() - 1));
        }
        y = fa[y];
    }
    return sum;
}
inline void push_into(int x, ll val)
{
    int depth = vis[x];
    update(tr[depth][x], 1, val);
    int y = fa[x];
    while(y)
    {
        depth--;
        update(tr[depth][y], dis_from_ff[x][depth] + 1, val);
        update(tr[depth][under_ff[x][depth]], dis_from_ff[x][depth] + 1, val);
        y = fa[y];
    }
}
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &N, &Q);
        Graph::init();
        Divide::Init();
        Segement::Clear();
        for(int i=1, u, v; i<N; i++)
        {
            scanf("%d%d", &u, &v);
            _add(u, v);
        }
        for(int i=1; i<=Q; i++) ques[i].In();
        all = N;
        find_root(1, 0);
        fa[root] = 0;
        dis_from_ff[root][1] = 0;
        under_ff[root][1] = root;
        node_Divide(root, 1);
        dfs_Deep(1, 0);
        for(int j=1; (1 << j) < N; j++)
        {
            for(int i=1; i<=N; i++) lca_fa[i][j] = lca_fa[lca_fa[i][j - 1]][j - 1];
        }
        sort(ques + 1, ques + Q + 1);
        Question now;
        ll sum, ans = 0;
        for(int i=1, x; i<=Q; i++)
        {
            now = ques[i];
            sum = ask(now.x, now.r);
            if(sum >= now.v) continue;
            ans += now.v - sum;
            x = now.x;
            for(int j=19; j>=0; j--) if((now.r >> j) & 1) x = lca_fa[x][j];
            if(!x) x = 1;
            push_into(x, now.v - sum);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值