題目鏈接:點(diǎn)擊查看
題目大意:給出一棵樹,再給出 m 次操作,每次操作會(huì)選擇兩個(gè)點(diǎn) ( x , y ) ,使得這條路徑上的所有點(diǎn)的種類 z 加一,最后問每個(gè)點(diǎn)的哪個(gè)種類出現(xiàn)的頻率最高,若多個(gè)種類出現(xiàn)頻率相同時(shí),輸出編號最小的
題目分析:線段樹合并模板題,感覺 update 函數(shù)和主席樹非常像,所以就仿照主席樹的寫法去實(shí)現(xiàn)的,相比于正常的線段樹來說,僅僅多了一個(gè) merge 函數(shù)用于合并兩棵線段樹罷了,時(shí)間復(fù)雜度是嚴(yán)格 nlogn 的,因?yàn)榫S護(hù)的時(shí)候采用的是樹上差分,即:
x + 1 y + 1 lca( x , y ) - 1 fa[ lca( x , y ) ] -1
對于某次操作共需要維護(hù) 4 條鏈,也就是說總共需要維護(hù) 4 * m * logn 條鏈,所以空間需要開 50 倍才夠,對于這個(gè)題目而言,在每個(gè)節(jié)點(diǎn)上都維護(hù)一棵線段樹然后 dfs 合并即可
代碼: ?
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#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>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node[N];int ans[N];int dp[N][20],deep[N];void dfs(int u,int fa,int dep)
{deep[u]=dep;dp[u][0]=fa;for(int i=1;i<20;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){if(v==fa)continue;dfs(v,u,dep+1);}
}int LCA(int x,int y)
{if(deep[x]<deep[y])swap(x,y);for(int i=19;i>=0;i--)if(deep[x]-deep[y]>=(1<<i))x=dp[x][i];if(x==y)return x;for(int i=19;i>=0;i--)if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}return dp[x][0];
}struct Node
{int l,r,mmax,id;
}tree[N*50];int root[N],tot;void pushup(int k)
{if(tree[tree[k].l].mmax>=tree[tree[k].r].mmax){tree[k].mmax=tree[tree[k].l].mmax;tree[k].id=tree[tree[k].l].id;}else{tree[k].mmax=tree[tree[k].r].mmax;tree[k].id=tree[tree[k].r].id;}
}void update(int& k,int pos,int val,int l,int r)
{if(!k)k=++tot;assert(tot<N*50);if(l==r){tree[k].mmax+=val;tree[k].id=l;return;}int mid=l+r>>1;if(pos<=mid)update(tree[k].l,pos,val,l,mid);elseupdate(tree[k].r,pos,val,mid+1,r);pushup(k);
}void merge(int& a,int b,int l,int r)
{if(!a||!b){a=a+b;return;}if(l==r){tree[a].mmax+=tree[b].mmax;tree[a].id=l;return;}int mid=l+r>>1;merge(tree[a].l,tree[b].l,l,mid);merge(tree[a].r,tree[b].r,mid+1,r);pushup(a);
}void dfs_ans(int u,int fa)
{for(auto v:node[u]){if(v==fa)continue;dfs_ans(v,u);merge(root[u],root[v],1,100000);}if(tree[root[u]].mmax)ans[u]=tree[root[u]].id;
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m);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);}dfs(1,0,0);while(m--){int x,y,z;scanf("%d%d%d",&x,&y,&z);int lca=LCA(x,y); update(root[x],z,1,1,100000);update(root[y],z,1,1,100000);update(root[lca],z,-1,1,100000);update(root[dp[lca][0]],z,-1,1,100000);}dfs_ans(1,0);for(int i=1;i<=n;i++)printf("%d\n",ans[i]);return 0;
}
?
總結(jié)
以上是生活随笔 為你收集整理的洛谷 - P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并(树上差分+线段树合并) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。