題目鏈接:點(diǎn)擊查看
題目大意:給出一棵樹,再給出 m 次操作,每次操作分為三種類型,dist( x , y ) 代表點(diǎn) x 和點(diǎn) y 之間的距離:
1 pos val:將點(diǎn) pos 位置的值增加 val ,將其余所有點(diǎn) x?的值,增加 val - dist( pos , x ) 2 pos:點(diǎn) pos 位置的值與 0 取 min 3 pos:查詢點(diǎn) pos 位置的值
題目分析:參考博客:https://blog.csdn.net/tianyizhicheng/article/details/107734665
第二個(gè)操作顯然是充數(shù)的,額外用一個(gè) delta 數(shù)組隨便維護(hù)一下就好了,主要是操作 1 和操作 3
對(duì)于每次操作 1 來(lái)說(shuō),肯定不能暴力去維護(hù)所有的 n 個(gè)點(diǎn),所以我們不妨將點(diǎn)權(quán)轉(zhuǎn)換為每個(gè)點(diǎn)與 root 節(jié)點(diǎn)( 設(shè)為點(diǎn) 1 )的相對(duì)權(quán)值
畫個(gè)圖然后分類討論一下吧,現(xiàn)在假設(shè)我們將點(diǎn) 6 的權(quán)值增加 w,那么顯然根節(jié)點(diǎn)(點(diǎn)1)的權(quán)值會(huì)增加 w - 2 ,我們將點(diǎn) 1 的權(quán)值記為 all ,此時(shí)也就是 all = w - 2,因?yàn)辄c(diǎn) 6 的深度為 2
如果我們想要求與點(diǎn) 6 的 lca 為 root 的點(diǎn)的權(quán)值,也就是點(diǎn) 1 , 2 , 3 , 4 的權(quán)值,顯然 all -?deep[ x ] 就是答案了,因?yàn)?lca 為 1 ,所以這些點(diǎn)與點(diǎn) 1 的距離增大,相應(yīng)的與點(diǎn) 6 的距離也會(huì)增大,答案自然也會(huì)變小
既然我們想讓答案與 deep 形成關(guān)系,對(duì)于那些,與點(diǎn) 6 的 lca 不為 root 的點(diǎn),如:點(diǎn) 5 , 6 , 7 ,也需要構(gòu)造一個(gè)公式,也就是 all - deep[ x ] + y 為點(diǎn) x 的權(quán)值,通過(guò)觀察不難發(fā)現(xiàn),這個(gè) y 值可以分兩種情況討論:
當(dāng)點(diǎn) x 位于 點(diǎn) 1 ~?點(diǎn) 6 的路徑上,即點(diǎn) 5 和?6 ,那么 y 的值為點(diǎn) 1 ~ 點(diǎn) x 的邊數(shù) * 2 否則,y 的值為點(diǎn) 1 ~ 點(diǎn) 6 的邊數(shù) * 2
綜上所述,對(duì)于操作 1 來(lái)說(shuō),需要執(zhí)行的操作就是:
all += w - deep[ x ] 將點(diǎn) 1 ~ 點(diǎn) x 這條路徑上的邊權(quán) + 2
對(duì)于操作 3 查詢來(lái)說(shuō),答案就是 all + ( 點(diǎn) 1 ~ 點(diǎn) x 這條邊上的邊權(quán)之和 ) - deep[ x ] * num
注意,這里的 deep 為什么突然乘以 num 了,解釋一下這個(gè)突然出現(xiàn)的 num ,其意義是操作 1 的數(shù)量,舉個(gè)很簡(jiǎn)單的例子,還是上圖,如果對(duì)點(diǎn) 6 進(jìn)行兩次操作 1 ,增加的權(quán)值都是 w ,那么此時(shí)的 all = ( w -? 2 ) * 2 ,如果要求點(diǎn) 2 的權(quán)值,答案應(yīng)該是 all - deep[ 2 ] * 2 而不是 all - deep[ 2 ]
剩下的就是實(shí)現(xiàn)了,對(duì)于樹上路徑的區(qū)間更改和區(qū)間查詢,可以利用樹鏈剖分和線段樹來(lái)執(zhí)行,因?yàn)槭且獙?duì)邊權(quán)操作,所以可以將邊權(quán)轉(zhuǎn)換為點(diǎn)權(quán),很基本的操作,直接實(shí)現(xiàn)就好了
操作 2 的 delta 就不多說(shuō)了,如果操作 1 明白了的話,操作 2 看一眼代碼應(yīng)該就會(huì)了
代碼: ?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=5e4+100;LL all,tot,delta[N];vector<int>node[N];int deep[N],fa[N],son[N],num[N];void dfs1(int u,int f,int dep)
{deep[u]=dep;son[u]=-1;num[u]=1;fa[u]=f;for(auto v:node[u]){if(v==f)continue;dfs1(v,u,dep+1);num[u]+=num[v];if(son[u]==-1||num[son[u]]<num[v])son[u]=v;}
}int id[N],idd[N],top[N],cnt;//id[節(jié)點(diǎn)]=編號(hào) idd[編號(hào)]=節(jié)點(diǎn) void dfs2(int u,int f,int root)
{top[u]=root;id[u]=++cnt;idd[cnt]=u;if(son[u]!=-1)dfs2(son[u],u,root);for(auto v:node[u]){if(v==f||v==son[u])continue;dfs2(v,u,v);}
}struct Node
{int l,r;LL lazy,sum;LL mid(){return l+r>>1;}LL cal_len(){return r-l+1;}
}tree[N<<2];void pushup(int k)
{tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}void pushdown(int k)
{LL lz=tree[k].lazy;tree[k<<1].sum+=tree[k<<1].cal_len()*lz;tree[k<<1|1].sum+=tree[k<<1|1].cal_len()*lz;tree[k<<1].lazy+=lz;tree[k<<1|1].lazy+=lz;tree[k].lazy=0;
}void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].sum=tree[k].lazy=0;if(l==r)return;int mid=tree[k].mid();build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}void update(int k,int l,int r)
{if(l>r)return;if(tree[k].r<l||tree[k].l>r)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].lazy+=2;tree[k].sum+=tree[k].cal_len()*2;return;}if(tree[k].lazy)pushdown(k);update(k<<1,l,r);update(k<<1|1,l,r);pushup(k);
}LL query(int k,int l,int r)
{if(l>r)return 0;if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].sum;if(tree[k].lazy)pushdown(k);return query(k<<1,l,r)+query(k<<1|1,l,r);
}void change(int x)
{while(top[x]!=1){update(1,id[top[x]],id[x]);x=fa[top[x]];}update(1,id[1]+1,id[x]);
}LL ask(int x)
{LL ans=0;while(top[x]!=1){ans+=query(1,id[top[x]],id[x]);x=fa[top[x]];}ans+=query(1,id[1]+1,id[x]);return ans;
}void init(int n)
{for(int i=0;i<=n;i++){delta[i]=0;node[i].clear();}cnt=tot=all=0;
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int w;cin>>w;while(w--){int n,m;scanf("%d%d",&n,&m);init(n);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}dfs1(1,0,0);dfs2(1,0,1);build(1,1,n);while(m--){int op;scanf("%d",&op);if(op==1){int x,w;scanf("%d%d",&x,&w);all+=w-deep[x];tot++;change(x);}else if(op==2){int x;scanf("%d",&x);LL temp=all+ask(x)-deep[x]*tot;if(temp>delta[x])delta[x]=temp;}else if(op==3){int x;scanf("%d",&x);printf("%lld\n",all+ask(x)-deep[x]*tot-delta[x]);}}}return 0;
}
?
總結(jié)
以上是生活随笔 為你收集整理的牛客多校7 - A National Pandemic(树链剖分+线段树) 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。