CF696E ...Wait for it... 题解

CF696E ...Wait for it... 题解

一道细节很多的树剖题,很适合拿来练习...

题意简述

给定一棵 \(n\) 个点的树,有 \(m\) 个物品,第 \(i\) 个物品位置在 \(c_i\),初始权值为 \(i\)

给定 \(q\) 个询问,询问有如下两种:

  • 1 u v k:对于树上的一条简单路径 \((u,v)\) 删除路径上所有物品中权值最小的 \(k\) 个物品,并输出。
  • 2 u k:将 \(u\) 这个子树内所有物皮的权值加 \(k\)

题解

因为每个物品最多只会被删一次,所以最多输出 \(n+q\) 个数,因此每次可以暴力枚举会删去哪些数。

接着再转换一下,每次求前 \(k\) 小,就相当于求路径上最小的,然后删掉,这个过程重复 \(k\) 次。于是问题就变成了子树加,路径查询最小值,所以用树剖来维护就好了。

具体地,对于每个点,将这个点里面的物品从大到小排序,然后维护一个队列,每次删掉某个点的队尾,再更新线段树的信息即可。可以将空点的权值设为正无穷,如果有一次查询到路径上最小值为正无穷,那么这条路径就没有物品了,需要直接 break

然后,这道题一些细节需要注意一下:

  • 相同权值的两个物品是比较所在节点编号,而不是本身的编号。
  • 一定要先确保树剖写对了再写后面的,不然很有可能要调很久。
  • \(INF\) 要开到足够大。
  • 注意单点更新时应该更新 \(id_x\),而不是 \(x\)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define ls x<<1
#define rs x<<1|1
#define lson x<<1,l,mid
#define rson x<<1|1,mid+1,r
#define ll long long
using namespace std;
const int N = 1e5+5;
const ll inf = 1e15;
int ans[N],c[N],hd[N],n,m,q,cnt,sum;
vector<int> a[N];
bool cmp(int x,int y){return x > y;}
struct edge{int to,nex;}e[N << 1];
void add(int u,int v)
{e[++cnt] = {v,hd[u]};hd[u] = cnt;}
int fa[N],d[N],siz[N],son[N],id[N],rk[N],top[N],tot;
struct node
{
    ll v;int id;
    friend bool operator <(node x,node y)
    {return x.v != y.v?x.v < y.v:c[x.id] < c[y.id];}
}t[N << 2];ll tag[N << 2];
node get(int x,int i)
{
    if(a[i].empty())return {inf,0};
    int id = a[i].back();return {id+tag[x],id};
}
void update(int x){t[x] = min(t[ls],t[rs]);}
void pushtag(int x,ll v)
{if(t[x].v != inf)t[x].v += v,tag[x] += v;}
void pushdown(int x)
{
    ll &v = tag[x];if(!v)return ;
    pushtag(ls,v);pushtag(rs,v);v = 0;
}
void build(int x,int l,int r)
{
    if(l == r){t[x] = get(x,rk[l]);return ;}
    int mid = l+r>>1;
    build(lson);build(rson);
    update(x);
}
void modify1(int x,int l,int r,int i)
{
    if(l == r){t[x] = get(x,rk[l]);return ;}
    int mid = l+r>>1;pushdown(x);
    if(i <= mid)modify1(lson,i);
    else modify1(rson,i);
    update(x);
}
void modify2(int x,int l,int r,int L,int R,ll v)
{
    if(L <= l&&r <= R){pushtag(x,v);return ;}
    int mid = l+r>>1;pushdown(x);
    if(L <= mid)modify2(lson,L,R,v);
    if(mid < R)modify2(rson,L,R,v);
    update(x);
}
node query(int x,int l,int r,int L,int R)
{
    if(L <= l&&r <= R)return t[x];
    int mid = l+r>>1;node ans = {inf,0};pushdown(x);
    if(L <= mid)ans = query(lson,L,R);
    if(mid < R)ans = min(ans,query(rson,L,R));
    return ans;
}
void dfs1(int u,int f)
{
    fa[u] = f;d[u] = d[f]+1;siz[u] = 1;
    for(int i = hd[u],v;i;i = e[i].nex)
    {
        if((v = e[i].to) == f)continue;
        dfs1(v,u);siz[u] += siz[v];
        if(siz[son[u]] < siz[v])son[u] = v;
    }
}
void dfs2(int u,int tp)
{
    id[u] = ++tot;rk[tot] = u;top[u] = tp;
    if(son[u])dfs2(son[u],tp);
    for(int i = hd[u],v;i;i = e[i].nex)
        if((v = e[i].to) != fa[u]&&v != son[u])dfs2(v,v);
}
node queryRange(int x,int y)
{
    node ans = {inf,0};
    while(top[x] != top[y])
    {
        if(d[top[x]] < d[top[y]])swap(x,y);
        ans = min(ans,query(1,1,n,id[top[x]],id[x]));
        x = fa[top[x]];
    }
    if(d[x] > d[y])swap(x,y);
    return min(ans,query(1,1,n,id[x],id[y]));;
}
void modifyTree(int x,ll v)
{modify2(1,1,n,id[x],id[x]+siz[x]-1,v);}
bool solve(int u,int v)
{
    node now = queryRange(u,v);int x = now.id;
    if(now.v == inf)return 0;
    ans[++sum] = x;a[c[x]].pop_back();
    return modify1(1,1,n,id[c[x]]),1;
}
inline int rd()
{
    char c;int f = 1;
    while(!isdigit(c = getchar()))if(c=='-')f = -1;
    int x = c-'0';
    while(isdigit(c = getchar()))x = x*10+(c^48);
    return x*f;
}
int main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    n = rd();m = rd();q = rd();
    for(int i = 1;i < n;i++)
    {int u = rd(),v = rd();add(u,v);add(v,u);}
    for(int i = 1;i <= m;i++)
        a[c[i] = rd()].push_back(i);
    for(int i = 1;i <= n;i++)
        sort(a[i].begin(),a[i].end(),cmp);
    dfs1(1,0);dfs2(1,1);build(1,1,n);
    while(q--)
    {
        int op = rd(),u = rd(),v,k;
        if(op == 1)
        {
            v = rd();k = rd();sum = 0;
            while(k--)if(!solve(u,v))break;
            printf("%d ",sum);
            for(int i = 1;i <= sum;i++)printf("%d ",ans[i]);puts("");
        }
        else modifyTree(u,rd());
    }
    return 0;
}
posted @ 2024-07-29 10:19  max0810  阅读(29)  评论(0)    收藏  举报