luogu題解 P4092 【[HEOI2016/TJOI2016]樹】樹鏈剖分
阿新 • • 發佈:2018-07-07
git ble while algorithm namespace val ios 路徑 dig
題目鏈接:
https://www.luogu.org/problemnew/show/P4092
瞎扯--\(O(Q \log^3 N)\)解法
這道先yy出了一個\(O(Q \log^3 N)\),的做法,先樹鏈剖分。
對於加標記操作,找到那個點所在的鏈,將其\(top\)標記一下,然後該點到根節點區間和+1.
對於查詢操作,先看這個點所在鏈有沒有標記,如果沒有,就一直向上跳直到找到一條標記了的鏈,然後在那條鏈上根據到根節點區間和進行倍增/二分
然後出去吃飯的時候忽然想到了\(O(Q \log^2 N)\)的解法,於是剛剛這個解法剛打完還沒有查錯,放在這做一個參考
代碼:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cctype> #include <algorithm> #include <cmath> #define ll long long #define ri register int using namespace std; const int maxn=100005; const int inf=0x7fffffff; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c==‘-‘; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x; return ; } int n,q; struct Edge{ int ne,to; }edge[maxn<<1]; int h[maxn],num_edge=0; inline void add_edge(int f,int t){ edge[++num_edge].ne=h[f]; edge[num_edge].to=t; h[f]=num_edge; return ; } int dep[maxn],fa[maxn],size[maxn],son[maxn],top[maxn],dfn[maxn],rnk[maxn],cnt=0; void dfs_1(int now){ int v;size[now]=1; for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now])continue; fa[v]=now,dep[v]=dep[now]+1; dfs_1(v); size[now]+=size[v]; if(!son[now]||size[son[now]]<size[v])son[now]=v; } return ; } void dfs_2(int now,int t){ int v;top[now]=t; dfn[now]=++cnt,rnk[cnt]=now; if(!son[now])return ; dfs_2(son[now],t); for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now]||v==son[now])continue; dfs_2(v,v); } return ; } int sum[maxn<<2],tag[maxn<<2],L,R,dta,ok[maxn]; void build(int now,int l,int r){ if(l==r){ sum[now]=ok[rnk[l]]; return ; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); return ; } void pushdown(int now,int ln,int rn){ if(tag[now]){ sum[now<<1]+=tag[now]*ln; sum[now<<1|1]+=tag[now]*rn; tag[now<<1]+=tag[now]; tag[now<<1|1]+=tag[now]; tag[now]=0; } return ; } void update(int now,int l,int r){ if(L<=l&&r<=R){ sum[now]+=dta*(r-l+1); tag[now]+=dta; return ; } int mid=(l+r)>>1; pushdown(now,mid-l+1,r-mid); if(L<=mid)update(now<<1,l,mid); if(mid<R)update(now<<1|1,mid+1,r); sum[now]=sum[now<<1]+sum[now<<1|1]; return ; } int query(int now,int l,int r){ if(L<=l&&r<=R){ return sum[now]; } int mid=(l+r)>>1,ans=0; pushdown(now,mid-l+1,r-mid); if(L<=mid)ans+=query(now<<1,l,mid); if(mid<R)ans+=query(now<<1|1,mid+1,r); sum[now]=sum[now<<1]+sum[now<<1|1]; return ans; } void update_path(int x,int y){ dta=1;ok[top[x]]=1;//該條鏈上有一個標記的點 while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); L=dfn[top[x]],R=dfn[x]; update(1,1,n); } if(dfn[x]<dfn[y])swap(x,y); L=dfn[x],R=dfn[y]; update(1,1,n); return ; } inline int solve(int x,int y){ int tmp,val,p=0,k=1,len,ans=0; bool flag=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); len=dfn[x]-dfn[top[x]]; if(ok[top[x]]){ L=dfn[top[x]],R=dfn[x], tmp=query(1,1,n); p=0,k=1,flag=0; while(k!=0){ L=dfn[x+p+k],R=dfn[x]; if(query(1,1,n)>tmp)flag=1,k=k>>1; else p=p+k,k=k<<1; while(p+k>len)k=k>>1; } if(flag)return ans+dfn[x+p]-dfn[x]; } ans+=len; x=fa[top[x]]; } if(dfn[x]>dfn[y])swap(x,y); L=dfn[x],R=dfn[y],len=dfn[y]-dfn[x]; tmp=query(1,1,n); p=0,k=1; //cout<<y<<endl; if(x==y)return ans; while(k!=0){ L=dfn[x+p+k],R=dfn[x]; if(query(1,1,n)>tmp)k=k>>1; else p=p+k,k=k<<1; //if(y==3)cout<<k<<‘ ‘<<p<<endl; while(p+k>len)k=k>>1; } return ans+dfn[x+p]-dfn[x]; } int main(){ char opt[5]; int x,y,z; read(n),read(q); for(ri i=1;i<n;i++){ read(x),read(y); add_edge(x,y); add_edge(y,x); } dep[1]=1,fa[1]=0; dfs_1(1); dfs_2(1,1); ok[dfn[1]]=1; build(1,1,n); while(q--){ scanf("%s",opt); if(opt[0]==‘C‘){ read(x); //cout<<x<<"-----"<<endl; update_path(1,x); } else{ read(x); //cout<<x<<"***"<<endl; printf("%d\n",solve(x,1)); } } return 0; }
分析---\(O(Q \log^2 N)\)解法
首先我想到了一個錯誤的解法,就是因為鏈是線段樹上一個連續的區間,每個\([dfn[x],dfn[top[x]]]\)線段樹區間有個\(mx\)值,表示,\(x\)到\(top[x]\)路徑中距離它最近標記的祖先,加標記時比較原有標記深度與新標記深度然後更新。查詢的時候查詢\(x\)到\(top[x]\)的區間最大之就可以了,如果沒有,就一直往上跳直至找到
然而這個解法有個錯誤我SB地沒有發現,就是你更新區間最大值時,\(x\)上的祖先節點也會被更新到(因為深度更小),再次感謝wjyyy和creed_兩位大佬指出我的錯誤
正解應該是更新子樹,將子樹的最大值更新,查詢照樣,相比於我錯誤的代碼只需改一句話
代碼:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cctype> #include <algorithm> #include <cmath> #define ll long long #define ri register int using namespace std; const int maxn=100005; const int inf=0x7fffffff; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c==‘-‘; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x; return ; } int n,q; struct Edge{ int ne,to; }edge[maxn<<1]; int h[maxn],num_edge=0; inline void add_edge(int f,int t){ edge[++num_edge].ne=h[f]; edge[num_edge].to=t; h[f]=num_edge; return ; } int dep[maxn],fa[maxn],size[maxn],son[maxn],top[maxn],dfn[maxn],rnk[maxn],cnt=0; void dfs_1(int now){ int v;size[now]=0; for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now])continue; fa[v]=now,dep[v]=dep[now]+1; dfs_1(v); size[now]+=size[v]; if(!son[now]||size[son[now]]<size[v])son[now]=v; } return ; } void dfs_2(int now,int t){ int v;top[now]=t; dfn[now]=++cnt,rnk[cnt]=now; if(!son[now])return ; dfs_2(son[now],t); for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now]||v==son[now])continue; dfs_2(v,v); } return ; } int mx[maxn<<2],L,R,dta; void build(int now,int l,int r){ if(l==r){ if(rnk[l]==1)mx[now]=1; else mx[now]=0; return ; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); if(dep[mx[now<<1]]>dep[mx[now<<1|1]]){ mx[now]=mx[now<<1]; } else mx[now]=mx[now<<1|1]; return ; } void update(int now,int l,int r){ if(L<=l&&r<=R){ if(dep[mx[now]]<dep[dta]){ mx[now]=dta; } return ; } int mid=(l+r)>>1; if(L<=mid)update(now<<1,l,mid); if(mid<R)update(now<<1|1,mid+1,r); if(dep[mx[now<<1]]>dep[mx[now<<1|1]]){ mx[now]=mx[now<<1]; } else mx[now]=mx[now<<1|1]; return ; } int query(int now,int l,int r){ if(L<=l&&r<=R){ return mx[now]; } int mid=(l+r)>>1,ans=0,tmp; if(L<=mid){ int tmp=query(now<<1,l,mid); if(dep[ans]<dep[tmp])ans=tmp; } if(mid<R){ int tmp=query(now<<1|1,mid+1,r); if(dep[ans]<dep[tmp])ans=tmp; } return ans; } void update_path(int x){ dta=x; //L=R=dfn[x]; L=dfn[x],R=dfn[x]+size[x]; update(1,1,n); return ; } int query_path(int x){ int ans=0; while(top[x]!=1){ L=dfn[top[x]],R=dfn[x]; ans=query(1,1,n); if(ans!=0)return ans; x=fa[top[x]]; } L=dfn[1],R=dfn[x]; ans=query(1,1,n); return ans; } int main(){ char opt[5]; int x,y,z; read(n),read(q); for(ri i=1;i<n;i++){ read(x),read(y); add_edge(x,y); add_edge(y,x); } dep[0]=-1,dep[1]=1,fa[1]=0; dfs_1(1); dfs_2(1,1); build(1,1,n); while(q--){ //cout<<q<<endl; scanf("%s",opt); if(opt[0]==‘C‘){ read(x); update_path(x); } else{ read(x); printf("%d\n",query_path(x)); } } return 0; }
luogu題解 P4092 【[HEOI2016/TJOI2016]樹】樹鏈剖分