DFS序例題+感受

长皆發表於2024-11-20

最近在學dfs序的實際應用,專開個blog記錄下感受
目前來看都是配合線段樹,從樹上問題變成區間問題

求和

https://ac.nowcoder.com/acm/problem/204871
題面:
已知有 \(n\) 個節點,有 \(n - 1\) 條邊,形成一個樹的結構。

給定一個根節點 \(k\),每個節點都有一個權值,節點 \(i\) 的權值為 \(v_i\)

\(m\) 個操作,操作有兩種型別:

1 \(ax\):表示將節點 \(a\) 的權值加上 \(x\)

2 \(a\):表示求 \(a\) 節點的子樹上所有節點的和(包括 \(a\) 節點本身
輸入:
第一行給出三個正整數 \(n, m, k\),表示樹的節點數、操作次數、和這棵樹的根節點。

第二行給出 \(n\) 個正整數,第 \(i\) 個正整數表示第 \(i\) 個節點的權值 \(val_i\)

下面 \(n - 1\) 行每行兩個正整數 \(u, v\),表示邊的兩個端點。

接下來 \(m\) 行,每行給出一個操作。
輸出:
對於每個型別為 2 的操作,輸出一行一個正整數,表示以a為根的子樹的所有節點的權值和
資料範圍:
\( \begin{align*} 1 &\leq n, m \leq 1e6, 1 \leq k \leq n \\ 1 &\leq u, v \leq n \\ 1 &\leq a \leq n \\ -1e6 &\leq val_i, x \leq 1e6 \end{align*} \)
樣例:
5 6 1
1 2 3 4 5
1 3
1 2
2 4
2 5
1 2 10
1 3 10
1 4 5
1 5 1
2 3
2 2
————————
13
27
感受:算是個很基本的入門題了,就是dfs序+線段樹,直接一遍過了

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                   long long 
#define lowbit(x) (x & -x)
//#define endl "\n"//                           互動題記得刪除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
struct s
{
	ll v,l,r;
	ll lazy;
}p[1000002<<2];
void build(ll i,ll l,ll r)
{
	p[i].v=0;
	p[i].l=l,p[i].r=r;
	if(l==r)
	return ;
	build(i<<1,l,(l+r)>>1);
	build(i<<1|1,(l+r>>1)+1,r);
}
void update(ll i,ll l,ll r,ll v)
{
	if(l==p[i].l&&r==p[i].r)
	{
		p[i].v+=v;
		return ;
	}
	ll mid=(p[i].l+p[i].r)>>1;
	if(l<=mid)
	update(i<<1,l,min(mid,r),v);
	if(r>=mid+1)
	update(i<<1|1,max(mid+1,l),r,v);
	p[i].v=p[i<<1].v+p[i<<1|1].v;
}
ll query(ll i,ll l,ll r)
{
	ll ans=0;
	if(l==p[i].l&&r==p[i].r)
	{
		ans+=p[i].v;
		return ans;
	}
	ll mid=(p[i].l+p[i].r)>>1;
	if(l<=mid)
	ans+=query(i<<1,l,min(mid,r));
	if(r>=mid+1)
	ans+=query(i<<1|1,max(mid+1,l),r);
	return ans;
}
ll a[1000002];
ll ne[2000002];
ll to[2000002];
ll h[2000002];
ll b[2000002];
ll cnt=0;
ll gs=0;
void add(ll l,ll r)
{
	to[cnt]=r;
	ne[cnt]=h[l];
	h[l]=cnt++;	
}
void dfs(ll k,ll fa)
{
	gs++;
	b[gs]=k;
	for(ll i=h[k];i>=0;i=ne[i])
	{
		if(to[i]==fa)continue;
		dfs(to[i],k);
	}
	gs++;
	b[gs]=k;
}
ll vis[2000002];
pair<ll,ll>ans[1000002];
int main()
{
	fio();
ll n,m,k;
cin>>n>>m>>k;
for(ll i=1;i<=n;i++)cin>>a[i];
memset(h,-1,sizeof h);
build(1,1,n);
for(ll i=1;i<=n-1;i++)
{
ll l,r;
cin>>l>>r;
add(l,r);
add(r,l);
}
dfs(k,0);
cnt=0;
ll last=0;
for(ll i=1;i<=gs;i++)
{
if(vis[b[i]]==0)
{
   cnt++;
update(1,cnt,cnt,a[b[i]]);
  vis[b[i]]=cnt;
  last=cnt;
}
else 
{
ans[b[i]]={vis[b[i]],last};
}
}
//cout<<query(1,1,5)<<endl;
while(m--)
{
	ll op;
	cin>>op;
	switch(op)
	{
		case 1:
		{
			ll x,y;
			cin>>x>>y;
			update(1,vis[x],vis[x],y);
			break;
		}
		case 2:
		{
			ll x;
			cin>>x;
			cout<<query(1,ans[x].first,ans[x].second)<<endl;
			break;
		}
	}
}
}

華華和月月種樹

https://ac.nowcoder.com/acm/problem/23051
題面:
華華看書瞭解到,一起玩養成類的遊戲有助於兩人培養感情。所以他決定和月月一起種一棵樹。因為華華現在也是資訊學高手了,所以他們種的樹是資訊學意義下的。

華華和月月一起維護了一棵動態有根樹,每個點有一個權值。剛開存檔的時候,樹上只有0號節點,權值為0。接下來有兩種操作:

操作1: 輸入格式 \(1i\),表示月月氪金使節點 \(i\) 長出了一個新的兒子節點,權值為0,編號為當前最大編號+1(也可以理解為,當前是第幾個操作 1,新節點的編號就是多少)。

操作2: 輸入格式 \(2ia\),表示華華上線做任務使節點 \(i\) 的子樹中所有節點(即它和它的所有子孫節點)權值加 \(a\)

但是月月有時會檢查華華有沒有認真維護這棵樹,會作出詢問:

詢問3: 輸入格式 \(3i\),華華需要給出 \(i\) 節點此時的權值。

華華當然有認真種樹了,不過還是希望能寫個程式以備不時之需。
輸入:
第一行一個正整數 \(M\),接下來 \(M\) 行,每行先輸入一個正整數 \(O\) 表示操作型別,再輸入一個非負整數 \(i\) 表示操作或詢問的節點編號,如果 \(O=2\),再輸入一個正整數 \(a\)
輸出:
對於每個詢問 \(3\),輸出一個非負整數表示詢問的答案。
樣例:
9
1 0
2 0 1
3 0
3 1
1 0
1 1
2 0 2
3 1
3 3
——————
1
1
3
2
資料範圍:
\(1 \leq M \leq 4 \times 10^5\),保證操作1的數量不超過 \(10^5\),保證操作2中的引數 \(a\) 滿足 \(1 \leq a \leq 999\)
感受:感覺不難,但是錯了快15次吧,為什麼呢?一是手敲線段樹,區間修改漏了每次都是修改區間值,二是認為可以二分要修改的範圍,這裡不是子樹巢狀子樹,所以二分不行。三是每次遇到新節點時,得減少值,這裡直接問了區間和,然後區間裡每個數減去區間和了。被自己逗笑了

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                   long long 
#define lowbit(x) (x & -x)
//#define endl "\n"//                           互動題記得刪除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 4e5 + 5;
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
struct s
{
	ll v, l, r;
	ll lazy;
}p[maxn << 2];
void build(ll i, ll l, ll r)
{
	p[i].v = 0;
	p[i].l = l, p[i].r = r;
	p[i].lazy = 0;
	if (l == r)
		return;
	build(i << 1, l, (l + r) >> 1);
	build(i << 1 | 1, (l + r >> 1) + 1, r);
}
void push_down(ll i)
{
	if (p[i].lazy)
	{
		p[i << 1].v += (p[i << 1].r - p[i << 1].l + 1) * p[i].lazy;
		p[i << 1 | 1].v += (p[i << 1 | 1].r - p[i << 1 | 1].l + 1) * p[i].lazy;
		p[i << 1 | 1].lazy += p[i].lazy;
		p[i << 1].lazy += p[i].lazy;
		p[i].lazy = 0;
	}
}
void update(ll i, ll l, ll r, ll v)
{
	if (l == p[i].l && r == p[i].r)
	{
		p[i].v += (r-l+1)*v;
		p[i].lazy += v;
		return;
	}
	push_down(i);
	ll mid = (p[i].l + p[i].r) >> 1;
	if (l <= mid)
		update(i << 1, l, min(mid, r), v);
	if (r >= mid + 1)
		update(i << 1 | 1, max(mid + 1, l), r, v);
	p[i].v = p[i << 1].v + p[i << 1 | 1].v;
}
ll query(ll i, ll l, ll r)
{
	ll ans = 0;
	if (l == p[i].l && r == p[i].r)
	{
		ans += p[i].v;
		return ans;
	}
	push_down(i);
	ll mid = (p[i].l + p[i].r) >> 1;
	if (l <= mid)
		ans += query(i << 1, l, min(mid, r));
	if (r >= mid + 1)
		ans += query(i << 1 | 1, max(mid + 1, l), r);
	return ans;
}
vector<ll>g[500000];
ll a[maxn];
ll b[maxn << 1];
ll cnt = 0;
ll gs = 0;
void dfs(ll k, ll fa)
{
	gs++;
	b[gs] = k;
	for (auto j : g[k])
	{
		dfs(j, k);
	}
	gs++;
	b[gs] = k;
}
ll vis[maxn];
ll vi[maxn];
ll d[maxn];
pair<ll, ll>ans[maxn];
struct u
{
	ll x;
	ll l, r;
}f[maxn];
int main()
{
	fio();
	ll n;
	cin >> n;
	build(1, 1, n);
	ll o = 1;
	ll cs = 0;
	for (ll i = 1; i <= n; i++)
	{
		cin >> f[i].x;
		if (f[i].x == 2)cin >> f[i].l >> f[i].r;
		else
		{
			cin >> f[i].l;
			if (f[i].x == 1)
			{
				f[i].r=o;
				g[f[i].l].push_back(o);	
				o++;
			}
		}
	}
	dfs(0, -1);
	cnt = 0;
	for (ll i = 1; i <= gs; i++)
	{
		if (vis[b[i]] == 0)
		{
			cnt++;
			vis[b[i]] = cnt;
			d[cnt] = vi[b[i]];
		}
		else
		{
			ans[b[i]] = { vis[b[i]],cnt };
		}
	}
	cnt = 0;
	ll fs=0;
	for (ll i = 1; i <= n; i++)
	{
		if (f[i].x == 1)
		{
			ll u=query(1,ans[f[i].r].first,ans[f[i].r].first);
			update(1, ans[f[i].r].first, ans[f[i].r].first, -u);
		}
		else if (f[i].x == 2)
		{
			update(1, ans[f[i].l].first, ans[f[i].l].second, f[i].r);
		}
		else if (f[i].x == 3)
		{
			cout << query(1, ans[f[i].l].first, ans[f[i].l].first) << endl;
		}
	}
}

相關文章