最近在學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;
}
}
}