SS241128D. 旅行 (tour)

liyixin發表於2024-11-28

SS241128D. 旅行 (tour)

題意

給你一棵 \(n\) 個點的以 \(1\) 為根的樹,每個結點有點權 \(a_i\)。有 \(m\) 次操作。操作分 \(4\) 種。

  1. 查詢 \(u\) 的點權。
  2. \(u,v\) 路徑上所有點 \(p\) 的點權 \(a_p \gets ka_p + b\)
  3. \(u\) 的子樹所有點 \(p\) 的點權 \(a_p \gets ka_p + b\)
  4. 令距離 \(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();
}

相關文章