Codeforces 983E - NN country(貪心+倍增優化)
阿新 • • 發佈:2021-07-11
貪心+倍增優化+二維數點,體力勞動
一道(絕對)偏簡單的 D1E,但是我怕自己過若干年(大霧)忘了自己的解法了,所以過來水篇題解(
首先考慮怎麼暴力地解決這個問題,不難發現我們每一步肯定會貪心,貪心地跳到所有經過當前點的公交線路中另一端最淺的位置,直到到達兩點的 \(\text{LCA}\) 為止。不難發現上述過程可以倍增優化,具體來說我們記 \(nxt_{i,j}\) 表示從 \(i\) 開始走 \(2^j\) 步最淺能夠到達哪裡,那麼我們可以一面樹剖求出經過每個點能夠到達深度最淺的節點,一面倍增往上跳知道跳到深度 \(<\text{LCA}(u,v)\) 的位置為止。
不過上述演算法有一個漏洞,就是在我們到達 \(\text{LCA}(u,v)\) 的前一步到達的點 \(u',v'\) 很可能已經可以通過某條公交線相連了,此時我們大可不必再花費 \(2\) 的代價跳到 \(\text{LCA}(u,v)\),直接一步就可以搞定,答案需減一。因此考慮再倍增求出 \(u,v\) 到達 \(\text{LCA}(u,v)\) 之前上一步到達的節點 \(u',v'\),不難發現,由於 \(u',v'\) 之間不存在祖先關係,因此 \(u',v'\) 存在公交線相連的充要條件是存在某個公交線,兩個端點分別在 \(u',v'\) 子樹內,這個可以 DFS 序+離線二維數點/線上主席樹求出。
時間複雜度 \(n\log^2n\),但顯然有複雜度更優秀/更好寫的 implementation,比方說樹剖換成 set
啟發式合併程式碼可以少三十多行,換成線段樹合併可以少一個 \(\log\)。
我竟然寫這麼短的題解,我怕不是精神不正常(大霧
程式碼(這種題我都能碼 171 行……zz 這題我實現得跟 sh*t 一樣):
const int MAXN=2e5; const int LOG_N=18; const int INF=0x3f3f3f3f; int n,m,qu,qn,hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],ec=0; void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;} int siz[MAXN+5],wson[MAXN+5],dep[MAXN+5],fa[MAXN+5][LOG_N+2]; int top[MAXN+5],dfn[MAXN+5],tim=0,edt[MAXN+5]; void dfs1(int x){ siz[x]=1; for(int e=hd[x];e;e=nxt[e]){ int y=to[e];dep[y]=dep[x]+1; dfs1(y);siz[x]+=siz[y]; if(siz[y]>siz[wson[x]]) wson[x]=y; } } void dfs2(int x,int tp){ top[x]=tp;dfn[x]=++tim;if(wson[x]) dfs2(wson[x],tp); for(int e=hd[x];e;e=nxt[e]) if(to[e]!=fa[x][0]&&to[e]!=wson[x]) dfs2(to[e],to[e]); edt[x]=tim; } int getlca(int x,int y){ while(top[x]^top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]][0]; } if(dep[x]<dep[y]) swap(x,y); return y; } struct node{int l,r,val,lz;} s[MAXN*4+5]; void pushup(int k){s[k].val=min(s[k<<1].val,s[k<<1|1].val);} void build(int k,int l,int r){ s[k].l=l;s[k].r=r;s[k].lz=INF;if(l==r) return s[k].val=INF,void(); int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k); } void pushdown(int k){ if(s[k].lz!=INF){ chkmin(s[k<<1].val,s[k].lz);chkmin(s[k<<1|1].val,s[k].lz); chkmin(s[k<<1].lz,s[k].lz);chkmin(s[k<<1|1].lz,s[k].lz); s[k].lz=INF; } } void modify(int k,int l,int r,int x){ if(l<=s[k].l&&s[k].r<=r){ chkmin(s[k].val,x);chkmin(s[k].lz,x); return; } pushdown(k);int mid=s[k].l+s[k].r>>1; if(r<=mid) modify(k<<1,l,r,x); else if(l>mid) modify(k<<1|1,l,r,x); else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x); pushup(k); } int query(int k,int x){ if(s[k].l==s[k].r) return s[k].val;pushdown(k); pushdown(k);int mid=s[k].l+s[k].r>>1; if(x<=mid) return query(k<<1,x); else return query(k<<1|1,x); } void jumpath(int x,int y,int v){ while(top[x]^top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); modify(1,dfn[top[x]],dfn[x],v); x=fa[top[x]][0]; } if(dep[x]<dep[y]) swap(x,y); modify(1,dfn[y],dfn[x],v); } int get_kanc(int x,int k){ for(int i=LOG_N;~i;i--) if(k>>i&1) x=fa[x][i]; return x; } int nt[MAXN+5][LOG_N+2],cnt[MAXN+5],ans[MAXN+5],mark[MAXN+5]; int step(int x,int d){ if(dep[x]<=d) return 0; if(dep[nt[x][LOG_N]]>d) return -1;int cnt=0; for(int i=LOG_N;~i;i--) if(dep[nt[x][i]]>d) x=nt[x][i],cnt|=(1<<i); return cnt+(dep[x]>d); } int get_kstp(int x,int k){ for(int i=LOG_N;~i;i--) if(k>>i&1) x=nt[x][i]; return x; } vector<int> pts[MAXN+5]; struct qry{ int x,y,t,id; bool operator <(const qry &rhs) const{ return x<rhs.x; } } q[MAXN*4+5]; void add_rec(int x1,int x2,int y1,int y2,int id){ // printf("%d %d %d %d %d\n",x1,x2,y1,y2,id); q[++qn]={x2,y2,1,id};q[++qn]={x1-1,y2,-1,id}; q[++qn]={x2,y1-1,-1,id};q[++qn]={x1-1,y1-1,1,id}; } struct fenwick{ int t[MAXN+5]; void add(int x,int v){for(int i=x;i<=n;i+=(i&(-i))) t[i]+=v;} int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;} } tr; int main(){ scanf("%d",&n); for(int i=2;i<=n;i++) scanf("%d",&fa[i][0]),adde(fa[i][0],i); dfs1(1);dfs2(1,1);build(1,1,n); for(int i=1;i<=n;i++) modify(1,dfn[i],dfn[i],dep[i]); scanf("%d",&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); pts[dfn[x]].pb(dfn[y]);pts[dfn[y]].pb(dfn[x]); // printf("(%d %d)\n",dfn[x],dfn[y]); jumpath(x,y,dep[getlca(x,y)]); } for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1]; for(int i=1;i<=n;i++) nt[i][0]=get_kanc(i,dep[i]-query(1,dfn[i])); for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) nt[j][i]=nt[nt[j][i-1]][i-1]; scanf("%d",&qu); for(int i=1;i<=qu;i++){ int x,y,l;scanf("%d%d",&x,&y);l=getlca(x,y); int sx=step(x,dep[l]),sy=step(y,dep[l]); if(!~sx||!~sy) ans[i]=-1; else{ ans[i]=sx+sy; if(sx!=0&&sy!=0){ int ax=get_kstp(x,sx-1); int ay=get_kstp(y,sy-1); add_rec(dfn[ax],edt[ax],dfn[ay],edt[ay],i); } } } // for(int i=1;i<=qu;i++) printf("%d\n",ans[i]); sort(q+1,q+qn+1);int cur=1; for(int i=1;i<=qn;i++){ while(cur<=q[i].x){ for(int y:pts[cur]) tr.add(y,1); cur++; } cnt[q[i].id]+=q[i].t*tr.query(q[i].y); // printf("%d %d %d\n",q[i].x,q[i].y,tr.query(q[i].y)); } for(int i=1;i<=qu;i++) printf("%d\n",ans[i]-(cnt[i]>0)); return 0; }