SS241128D. 旅行 (tour)
題意
給你一棵 \(n\) 個點的以 \(1\) 為根的樹,每個結點有點權 \(a_i\)。有 \(m\) 次操作。操作分 \(4\) 種。
- 查詢 \(u\) 的點權。
- 令 \(u,v\) 路徑上所有點 \(p\) 的點權 \(a_p \gets ka_p + b\)。
- 令 \(u\) 的子樹所有點 \(p\) 的點權 \(a_p \gets ka_p + b\)。
- 令距離 \(u\) 不超過 \(d\) 的所有點 \(p\) 的點權 \(a_p \gets ka_p + b\)。
其中 \(d \le 10, n,m \le 10^5\)。
思路
因為有鏈和子樹操作,容易想到重剖。
題解說有這兩個操作,限定了只能在 dfs 序上做。
發現 \(d \le 10\),考慮比較暴力地維護操作 \(4\)。
如果 \(k=1\),那麼操作就具有交換律和結合律,可以分別處理鏈剖分和鄰域操作,鄰域操作可以對每個點打 \(tag_{0 \sim 10}\) 表示給 \(u\) 的子樹裡距離 \(u\) 為 \(0 \sim 10\) 的結點的標記。修改的時候就修改 \(u\) 和它的 \(d\) 層祖先,注意容斥一下,查詢的時候就訪問它的 \(d\) 層祖先。因為操作滿足交換律,使用標記永久化以保證時間複雜度。
時間複雜度 \(O(m (\log^2 n + d))\),寫得醜可能會變成 \(d^2\)。
但是這裡的操作雖然滿足結合率,不滿足交換律。不標記永久化時間複雜度會退化成單次 \(O(n)\)。
我們必須要在 dfs 序上維護這些操作。考慮剛剛的標記其實給一棵子樹對應的一段連續的 dfs 序打上對應深度的標記。我們可以線上段樹裡打這些標記。如何下放標記?直接給兩個兒子下放,和線段樹下放標記差不多。
時間複雜度 \(O(m(\log^2 nd+\log nd^2))\)。常數小,\(1s\) 內可以跑完。
code
感覺不是很好寫啊。
其實還行,參考了 std 的寫法。發現我的樹剖馬蜂屬實詭異。原來別人的樹剖和我的寫法有一些不同,我的樹剖寫了很多廢的東西啊,這就去改板子。
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace humorous {
#define isdigit(x) (x>='0'&&x<='9')
#define gc getchar_unlocked
#define pc putchar_unlocked
template <typename T>
void read(T &x) {
x=0;
char ch=gc();
for(;!isdigit(ch);ch=gc());
for(;isdigit(ch);ch=gc()) x=(x<<3)+(x<<1)+(ch^48);
}
template <typename T>
void write(T x,char ch) {
static int st[40];
int top=0;
do {
st[top++]=x%10;
x/=10;
}while(x);
while(top) pc(st[--top]^48);
pc(ch);
}
constexpr int N=1e5+7,mod=998244353,B=10;
int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
void _add(int &a,int b) { a=add(a,b); }
int mul(int a,int b) { return 1ll*a*b%mod; }
void _mul(int &a,int b) { a=mul(a,b); }
int sub,n,m;
int u,v,a[N],op;
int k,b,d;
vector<int> to[N];
int fa[N],dep[N];
int cnt;
int dfn[N],idfn[N],top[N],eddfn[N];
int siz[N],gson[N];
void dfs(int u,int f) {
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(int v:to[u]) if(v^f) {
dfs(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[gson[u]]) gson[u]=v;
}
}
void dfs(int u) {
dfn[u]=++cnt;
idfn[cnt]=u;
if(gson[u]) top[gson[u]]=top[u], dfs(gson[u]);
for(int v:to[u]) if((v^fa[u]) && (v^gson[u])) top[v]=v, dfs(v);
eddfn[u]=cnt;
}
struct pii{
int x,y;
pii operator * (pii b) const { return {mul(x,b.x), add(mul(y,b.x),b.y)}; }
};
pii tr[N<<2][12];
int mindep[N<<2];
void build(int u,int l,int r) {
rep(i,0,11) tr[u][i].x=1;
if(l==r) return mindep[u]=dep[idfn[l]], tr[u][0]={0,a[idfn[l]]}, void(0);
int mid=(l+r)>>1;
build(u<<1,l,mid), build(u<<1|1,mid+1,r);
mindep[u]=min(mindep[u<<1],mindep[u<<1|1]);
}
void init() {
dfs(1,0);
top[1]=1;
dfs(1);
build(1,1,n);
}
void f(int u,pii tg,int dep) {
if(dep>=mindep[u]) tr[u][dep-mindep[u]]=tr[u][dep-mindep[u]]*tg;
}
void pushdown(int u) {
rep(i,0,B) {
if(tr[u][i].x!=1 || tr[u][i].y) {
f(u<<1,tr[u][i],mindep[u]+i);
f(u<<1|1,tr[u][i],mindep[u]+i);
tr[u][i]={1,0};
}
}
if(tr[u][11].x!=1 || tr[u][11].y) {
int tmp=max(0,mindep[u]+11-mindep[u<<1]);
rep(i,tmp,11) tr[u<<1][i]=tr[u<<1][i]*tr[u][11];
tmp=max(0,mindep[u]+11-mindep[u<<1|1]);
rep(i,tmp,11) tr[u<<1|1][i]=tr[u<<1|1][i]*tr[u][11];
tr[u][11]={1,0};
}
}
int query(int u,int l,int r,int x) {
if(l==r) return tr[u][0].y;
int mid=(l+r)>>1;
pushdown(u);
if(x<=mid) return query(u<<1,l,mid,x);
return query(u<<1|1,mid+1,r,x);
}
void _update(int u,int l,int r,int L,int R,pii x) {
if(l>=L && r<=R) {
rep(i,0,11) tr[u][i]=tr[u][i]*x;
return;
}
int mid=(l+r)>>1;
pushdown(u);
if(L<=mid) _update(u<<1,l,mid,L,R,x);
if(mid+1<=R) _update(u<<1|1,mid+1,r,L,R,x);
}
void _update2(int u,int l,int r,int L,int R,int dep,pii x) {
if(mindep[u]>dep) return;
if(l>=L&&r<=R) return tr[u][dep-mindep[u]]=tr[u][dep-mindep[u]]*x, void(0);
int mid=(l+r)>>1;
pushdown(u);
if(L<=mid) _update2(u<<1,l,mid,L,R,dep,x);
if(mid+1<=R) _update2(u<<1|1,mid+1,r,L,R,dep,x);
}
void update2(int u,int v,pii x) {
while(top[u]^top[v]) {
if(dep[top[u]]<dep[top[v]]) swap(u,v);
_update(1,1,n,dfn[top[u]],dfn[u],x), u=fa[top[u]];
}
if(dfn[u]>dfn[v]) swap(u,v);
_update(1,1,n,dfn[u],dfn[v],x);
}
void update3(int u,pii x) { _update(1,1,n,dfn[u],eddfn[u],x); }
void update4(int u,int d,pii x) {
while(~d && u) {
if(d && fa[u]) {
_update2(1,1,n,dfn[u],eddfn[u],dep[u]+d,x);
_update2(1,1,n,dfn[u],eddfn[u],dep[u]+d-1,x);
}else {
rep(i,0,d) _update2(1,1,n,dfn[u],eddfn[u],dep[u]+i,x);
}
u=fa[u], --d;
}
}
void main() {
read(sub),read(n),read(m);
rep(i,1,n-1) {
read(u),read(v);
to[u].push_back(v), to[v].push_back(u);
}
rep(i,1,n) read(a[i]);
init();
rep(i,1,m) {
read(op), read(u);
if(op==1) write(query(1,1,n,dfn[u]),'\n');
else if(op==2) read(v),read(k),read(b), update2(u,v,{k,b});
else if(op==3) read(k),read(b), update3(u,{k,b});
else read(d),read(k),read(b), update4(u,d,{k,b});
}
}
}
int main() {
#ifdef LOCAL
freopen("my.out","w",stdout);
#else
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
#endif
humorous :: main();
}