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;
}

浙公网安备 33010602011771号