題目鏈接:點擊查看
題目大意:給出一棵樹,再給出 m 次操作,每次操作分為三種類型,dist( x , y ) 代表點 x 和點 y 之間的距離:
1 pos val:將點 pos 位置的值增加 val ,將其余所有點 x?的值,增加 val - dist( pos , x ) 2 pos:點 pos 位置的值與 0 取 min 3 pos:查詢點 pos 位置的值
題目分析:參考博客:https://blog.csdn.net/tianyizhicheng/article/details/107734665
第二個操作顯然是充數的,額外用一個 delta 數組隨便維護一下就好了,主要是操作 1 和操作 3
對于每次操作 1 來說,肯定不能暴力去維護所有的 n 個點,所以我們不妨將點權轉換為每個點與 root 節點( 設為點 1 )的相對權值
畫個圖然后分類討論一下吧,現在假設我們將點 6 的權值增加 w,那么顯然根節點(點1)的權值會增加 w - 2 ,我們將點 1 的權值記為 all ,此時也就是 all = w - 2,因為點 6 的深度為 2
如果我們想要求與點 6 的 lca 為 root 的點的權值,也就是點 1 , 2 , 3 , 4 的權值,顯然 all -?deep[ x ] 就是答案了,因為 lca 為 1 ,所以這些點與點 1 的距離增大,相應的與點 6 的距離也會增大,答案自然也會變小
既然我們想讓答案與 deep 形成關系,對于那些,與點 6 的 lca 不為 root 的點,如:點 5 , 6 , 7 ,也需要構造一個公式,也就是 all - deep[ x ] + y 為點 x 的權值,通過觀察不難發現,這個 y 值可以分兩種情況討論:
當點 x 位于 點 1 ~?點 6 的路徑上,即點 5 和?6 ,那么 y 的值為點 1 ~ 點 x 的邊數 * 2 否則,y 的值為點 1 ~ 點 6 的邊數 * 2
綜上所述,對于操作 1 來說,需要執行的操作就是:
all += w - deep[ x ] 將點 1 ~ 點 x 這條路徑上的邊權 + 2
對于操作 3 查詢來說,答案就是 all + ( 點 1 ~ 點 x 這條邊上的邊權之和 ) - deep[ x ] * num
注意,這里的 deep 為什么突然乘以 num 了,解釋一下這個突然出現的 num ,其意義是操作 1 的數量,舉個很簡單的例子,還是上圖,如果對點 6 進行兩次操作 1 ,增加的權值都是 w ,那么此時的 all = ( w -? 2 ) * 2 ,如果要求點 2 的權值,答案應該是 all - deep[ 2 ] * 2 而不是 all - deep[ 2 ]
剩下的就是實現了,對于樹上路徑的區間更改和區間查詢,可以利用樹鏈剖分和線段樹來執行,因為是要對邊權操作,所以可以將邊權轉換為點權,很基本的操作,直接實現就好了
操作 2 的 delta 就不多說了,如果操作 1 明白了的話,操作 2 看一眼代碼應該就會了
代碼: ?
#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[節點]=編號 idd[編號]=節點 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;
}
?
總結
以上是生活随笔 為你收集整理的牛客多校7 - A National Pandemic(树链剖分+线段树) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。