题解 P9320【[EGOI2022] Tourists】
problem
一个长为 \(m\) 的数组 \(a\),每个数的取值为 \([1,n]\) 的正整数;另外有一个长为 \(m\) 的数组 \(b\),初始全零;另外有一棵 \(n\) 个点的树,求树上两点距离的函数为 \(dist\)。请支持三种操作:
- 输入 \(l,r,c\),枚举 \(i\in [l,r]\),使得 \(b_i\gets b_i-dist(a_i,c)\),然后 \(a_i\gets c\)。
- 输入 \(c,d\),对所有 \(a_i=c\) 的 \(i\),使得 \(b_i\gets b_i+d\)。
- 输入 \(i\),查询 \(b_i\)。
数据范围全部为二十万。
solution
如果没有操作二,我们可以颜色段均摊(或者叫“珂朵莉树”):用 std::set
暴力的进行区间推平。但是现在有这样的操作,会使得复杂度假掉。
考虑改成线段树,在线段树上进行区间覆盖,代替这个颜色段均摊的过程。
我们想象,对于一个 \(a_i\),它的每一次修改(从 \(a_i=c\) 改为 \(d\)),会使得 \(b\) 增加 \(-dist(c,d)\) 加上这一段时间城市 \(c\) 产生的价值。
也就是,对于一个点,我们想象打在它身上的是一堆形如 \((t,u)\) 的操作,表示在 \(t\) 时刻这个人走到 \(u\) 点,它的价值可以通过计算两个操作的价值得到。
(定义函数 \(f:(t_1,u_1)\to(t_2,u_2)\),表示前一个操作产生的总贡献,这个是用可持久化线段树)
这一堆操作,是可以合并的:我们将操作打包称为操作包,记录第一个操作和最后一个操作,以及总共产生的贡献;那么两个操作包合并的时候,只需要计算前一个的最后一个和后一个的第一个的贡献即可。
将这个操作包作为懒标记打到线段树上即可。总的时间复杂度是 \(O(n\log^2 n)\)。
code
点击查看代码
不知道那些 1s 的人怎么过的,我只能 4s。
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<int N> struct egoi2022{
int ch[N+10][2],tot; LL val[N+10];
int newnode(int q=0){int p=++tot;return memcpy(ch[p],ch[q],sizeof ch[0]),val[p]=val[q],p;}
egoi2022():tot(0){val[0]=0,memset(ch[0],0,sizeof ch[0]);}
void build(int &p,int l,int r){
p=newnode();
if(l==r) return ;
int mid=(l+r)>>1;
build(ch[p][0],l,mid),build(ch[p][1],mid+1,r);
}
int modify(int x,LL k,int p,int l,int r){
int q=newnode(p);
if(l==r) return val[q]+=k,q;
int mid=(l+r)>>1;
if(x<=mid) ch[q][0]=modify(x,k,ch[p][0],l,mid);
else ch[q][1]=modify(x,k,ch[p][1],mid+1,r);
return q;
}
LL query(int x,int p,int l,int r){
if(!p||l==r) return val[p];
int mid=(l+r)>>1;
if(x<=mid) return query(x,ch[p][0],l,mid);
else return query(x,ch[p][1],mid+1,r);
}
};
struct node{
// int t,u; LL s;//规定 t=0 为空状态
int tl,tr,ul,ur; LL s;
};
node O={0,0,0,0,0};
LL calc(int lt,int rt,int u);
template<int N> struct segtree{
node tag[N<<2];
void build(int a[],int p,int l,int r){
if(l==r) return tag[p]={1,1,a[l],a[l],0ll},void();
int mid=(l+r)>>1; tag[p]=O;
build(a,p<<1,l,mid),build(a,p<<1|1,mid+1,r);
}
void add(node k,int p){tag[p]+=k;}
void pushdown(int p){add(tag[p],p<<1),add(tag[p],p<<1|1),tag[p]=O;}
void modify(int L,int R,node k,int p,int l,int r){
if(L<=l&&r<=R) return add(k,p);
int mid=(l+r)>>1; pushdown(p);
if(L<=mid) modify(L,R,k,p<<1,l,mid);
if(mid<R) modify(L,R,k,p<<1|1,mid+1,r);
}
LL query(int tim,int x,int p,int l,int r){
if(l==r) return tag[p].s+calc(tag[p].tr,tim,tag[p].ur);
int mid=(l+r)>>1; pushdown(p);
if(x<=mid) return query(tim,x,p<<1,l,mid);
else return query(tim,x,p<<1|1,mid+1,r);
}
};
int n,m,Q,a[1<<18],root[1<<18],fa[19][1<<18],dep[1<<18];
vector<int> g[1<<18];
segtree<1<<18> t;
egoi2022<1<<23> T;
void dfs(int u,int f=0){fa[0][u]=f,dep[u]=dep[f]+1; for(int v:g[u]) if(v!=f) dfs(v,u);}
int lca(int u,int v){
int d=dep[u]-dep[v]; if(d<0) swap(u,v),d=-d;
for(int j=18;j>=0;j--) if(d>>j&1) u=fa[j][u];
if(u==v) return u;
for(int j=18;j>=0;j--) if(fa[j][u]!=fa[j][v]) u=fa[j][u],v=fa[j][v];
return fa[0][u];
}
int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
LL calc(int lt,int rt,int u){return T.query(u,root[rt],1,n)-T.query(u,root[lt-1],1,n);}
bool operator==(node a,node b){
return a.tl==b.tl&&a.tr==b.tr&&a.ul==b.ul&&a.ur==b.ur&&a.s==b.s;
}
node operator+=(node &a,node b){
if(b==O) return a; else if(a==O) return a=b;
return a={
a.tl,b.tr,a.ul,b.ur,
a.s+b.s-dist(a.ur,b.ul)+calc(a.tr,b.tl-1,a.ur)
};
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
t.build(a,1,1,m);
T.build(root[0],1,n);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
dfs(1);
for(int j=1;j<=18;j++) for(int i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]];
for(int i=2;i<=Q+1;i++){
root[i]=root[i-1];
char op;
scanf(" %c",&op);
if(op=='t'){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
t.modify(l,r,{i,i,c,c,0},1,1,m);
}else if(op=='e'){
int c,d;
scanf("%d%d",&c,&d);
root[i]=T.modify(c,d,root[i],1,n);
}else{
int x;
scanf("%d",&x);
printf("%lld\n",t.query(i,x,1,1,m));
}
}
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-P9320.html