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