洛谷 P3979 遥远的国度 做题记录

前置芝士:树链剖分

思路

我们先随手画出一张图:

我们首先以 \(1\) 为根构造这颗树。
这张图比较特殊,因为这张图的编号同时也是他的 dfn 序。

我们将其分类讨论。
image
设当前根节点为 \(rt\),查询的节点为 \(x\),那么:

  • \(rt=x\) 时(图中蓝圈部分),我们可以访问所有的节点,那么就相当于是求全局最小值。
  • \(x\) 不在 \(1\)\(rt\) 的路径上时(图中橙圈部分),不同的根并未对这些造成影响。
  • \(x\)\(1\)\(rt\) 的路径上时(图中绿圈部分),他覆盖不到 \(x\)\(rt\) 路径上的最接近的儿子为根的树。

那么剩下就很好做了。

时间复杂度:\(O(n \log^2 n)\)

点击查看代码
#include<bits/stdc++.h>

#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 100050

struct SegT {
	struct node {
		int mn,tag;
	}tr[maxn<<2];
	void psu(int idx) {
		tr[idx].mn=min(tr[lc(idx)].mn,tr[rc(idx)].mn);
	}
	void psd(int idx) {
		if(tr[idx].tag==-1) return ;
		tr[lc(idx)].mn=tr[rc(idx)].mn=tr[idx].tag;
		tr[lc(idx)].tag=tr[rc(idx)].tag=tr[idx].tag;
		tr[idx].tag=-1;
	}
	void build(int idx,int l,int r,int a[]) {
		tr[idx].tag=-1;
		if(l==r) {
			tr[idx].mn=a[l];
			return ;
		}
		int mid=(l+r)>>1;
		build(lc(idx),l,mid,a);
		build(rc(idx),mid+1,r,a);
		psu(idx);
	}
	void upd(int idx,int l,int r,int L,int R,int val) {
		if(L<=l&&r<=R) {
			tr[idx].mn=tr[idx].tag=val;
			return ;
		}
		psd(idx);
		int mid=(l+r)>>1;
		if(L<=mid) upd(lc(idx),l,mid,L,R,val);
		if(R>mid) upd(rc(idx),mid+1,r,L,R,val);
		psu(idx);
	}
	int query(int idx,int l,int r,int L,int R) {
		if(L<=l&&r<=R) return tr[idx].mn;
		psd(idx);
		int mid=(l+r)>>1,res=2141483647;
		if(L<=mid) res=query(lc(idx),l,mid,L,R);
		if(R>mid) res=min(res,query(rc(idx),mid+1,r,L,R));
		return res;
	}
}Tr;

int n,m,w[maxn];
vector<int> G[maxn];

struct TreDec {
	int siz,mxs,fa,dep;
	int dfn,top;
}tr[maxn];

void dfs1(int u,int fa) {
	tr[u].dep=tr[fa].dep+1;
	tr[u].siz=1;
	tr[u].fa=fa;
	for(auto v:G[u]) if(v!=fa) {
		dfs1(v,u);
		tr[u].siz+=tr[v].siz;
		if(tr[tr[u].mxs].siz<tr[v].siz) tr[u].mxs=v;
	}
}

int dfncnt,dfnval[maxn];
void dfs2(int u,int Ltop) {
	tr[u].top=Ltop;
	tr[u].dfn=++dfncnt;
	dfnval[dfncnt]=w[u];
	if(tr[u].mxs!=0) dfs2(tr[u].mxs,Ltop);
	for(auto v:G[u]) if(v!=tr[u].fa&&v!=tr[u].mxs) dfs2(v,v);
}
int rt=1;

void updL(int x,int y,int val) {
	while(tr[x].top!=tr[y].top) {
		if(tr[tr[x].top].dep<tr[tr[y].top].dep) swap(x,y);
		Tr.upd(1,1,n,tr[tr[x].top].dfn,tr[x].dfn,val);
		x=tr[tr[x].top].fa;
	}
	if(tr[x].dep>tr[y].dep) swap(x,y);
	Tr.upd(1,1,n,tr[x].dfn,tr[y].dfn,val);
}

int fnd(int x) {
	if(x==rt) return -1;
	if(tr[x].dfn>=tr[rt].dfn||tr[x].dfn+tr[x].siz-1<tr[rt].dfn) return 0;
	int u=rt;
	while(tr[u].top!=tr[x].top) {
		if(tr[tr[u].top].fa==x) return tr[u].top;
		u=tr[tr[u].top].fa;
	}
	return tr[x].mxs;
}

int query(int x) {
	int fin=fnd(x);
	if(fin==-1) return Tr.tr[1].mn;
	if(fin==0) return Tr.query(1,1,n,tr[x].dfn,tr[x].dfn+tr[x].siz-1);
	int ans=Tr.query(1,1,n,1,tr[fin].dfn-1);
	if(tr[fin].dfn+tr[fin].siz-1<n) ans=min(ans,Tr.query(1,1,n,tr[fin].dfn+tr[fin].siz,n));
	return ans;
}

void work() {
	in2(n,m);
	For(i,1,n-1) {
		int u,v;
		in2(u,v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	For(i,1,n) in1(w[i]);
	in1(rt);
	dfs1(1,0);
	dfs2(1,1);
	Tr.build(1,1,n,dfnval);
	while(m--) {
		int opt=read(); 
		if(opt==1) in1(rt);
		else if(opt==2) {
			int x,y,d;
			in3(x,y,d);
			updL(x,y,d);
		} else {
			int x=read();
			cout<<query(x)<<'\n';
		}
	}
}

signed main() {
//	freopen("data.in","r",stdin);
//	freopen("myans.out","w",stdout);
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	double stt=clock();
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	cerr<<"\nTotal Time is:"<<(clock()-stt)*1.0/1000<<" second(s)."<<'\n';
	return 0;
}
posted @ 2025-03-14 17:04  coding_goat_qwq  阅读(27)  评论(0)    收藏  举报