1. 程式人生 > >[ZJOI2019]語言(樹鏈剖分+動態開點線段樹+啟發式合並)

[ZJOI2019]語言(樹鏈剖分+動態開點線段樹+啟發式合並)

dfs != turn isp 答案 std names namespace 線段樹

首先,對於從每個點出發的路徑,答案一定是過這個點的路徑所覆蓋的點數。然後可以做樹上差分,對每個點記錄路徑產生總貢獻,然後做一個樹剖維護,對每個點維護一個動態開點線段樹。最後再從根節點開始做一遍dfs,把每個節點對應的線段樹啟發式合並即可。時空復雜度均為O(nlog2n)。聽說還有一個log的做法,但感覺太神仙不會,不過2個log能過就不管了。

技術分享圖片
#include<bits/stdc++.h>
#define lson l,mid,tr[rt].lc
#define rson mid+1,r,tr[rt].rc
using namespace std;
const int
N=1e5+7; struct Seg{int lc,rc,tag,sz;}tr[N*300]; int n,m,cnt,fa[N],sz[N],dep[N],son[N],top[N],dfn[N],rt[N]; long long ans; vector<int>G[N]; void dfs1(int u,int f) { sz[u]=1,dep[u]=dep[f]+1,fa[u]=f; for(int i=0;i<G[u].size();i++) if(G[u][i]!=f) { dfs1(G[u][i],u),sz[u]
+=sz[G[u][i]]; if(sz[son[u]]<sz[G[u][i]])son[u]=G[u][i]; } } void dfs2(int u,int tp) { top[u]=tp,dfn[u]=++cnt; if(son[u])dfs2(son[u],tp); for(int i=0;i<G[u].size();i++)if(G[u][i]!=fa[u]&&G[u][i]!=son[u])dfs2(G[u][i],G[u][i]); } void pushup(int rt,int len) {
if(tr[rt].tag)tr[rt].sz=len;else tr[rt].sz=tr[tr[rt].lc].sz+tr[tr[rt].rc].sz;} void modify(int rt,int v,int len){tr[rt].tag+=v;pushup(rt,len);} void update(int L,int R,int v,int l,int r,int&rt) { if(!rt)rt=++cnt; if(L<=l&&r<=R){modify(rt,v,r-l+1);return;} int mid=l+r>>1; if(L<=mid)update(L,R,v,lson); if(R>mid)update(L,R,v,rson); pushup(rt,r-l+1); } void Update(int&rt,int x,int y,int v) { while(top[x]!=top[y])update(dfn[top[x]],dfn[x],v,1,n,rt),x=fa[top[x]]; if(x!=y)update(dfn[y]+1,dfn[x],v,1,n,rt); } int lca(int x,int y) { while(top[x]!=top[y])if(dep[top[x]]<dep[top[y]])y=fa[top[y]];else x=fa[top[x]]; return dep[x]<dep[y]?x:y; } int merge(int u,int v,int l,int r) { if(!u||!v)return u+v; tr[u].tag+=tr[v].tag; if(l==r){pushup(u,1);return u;} int mid=l+r>>1; tr[u].lc=merge(tr[u].lc,tr[v].lc,l,mid); tr[u].rc=merge(tr[u].rc,tr[v].rc,mid+1,r); pushup(u,r-l+1); return u; } void dfs3(int u,int f) { for(int i=0;i<G[u].size();i++) if(G[u][i]!=f)dfs3(G[u][i],u),rt[u]=merge(rt[u],rt[G[u][i]],1,n); ans+=max(tr[rt[u]].sz-1,0); } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x); dfs1(1,0); dfs2(1,1); cnt=0; for(int i=1,x,y,f;i<=m;i++) { scanf("%d%d",&x,&y); f=lca(x,y); Update(rt[x],x,fa[f],1),Update(rt[x],y,f,1); Update(rt[y],x,fa[f],1),Update(rt[y],y,f,1); Update(rt[f],x,fa[f],-1),Update(rt[f],y,f,-1); if(fa[f])Update(rt[fa[f]],x,fa[f],-1),Update(rt[fa[f]],y,f,-1); } dfs3(1,0); cout<<ans/2; }
View Code

[ZJOI2019]語言(樹鏈剖分+動態開點線段樹+啟發式合並)