1. 程式人生 > >bzoj2035: [2009國家集訓隊]數據讀取問題

bzoj2035: [2009國家集訓隊]數據讀取問題

nlogn 2009國家集訓隊 output tput lin desc urn class jpg

Description

技術分享

Input

技術分享

Output

技術分享

可以轉為邊權為1的最短路:將不修改並讀取x個數看作有向邊,原先樹上的邊仍保留且視為雙向邊(但從根出發的邊為單向)表示上次讀取的修改

第一種邊是點到bfs序的一個區間區間連邊,用並查集維護bfs序中每個位置下一個未處理的位置即可

為了求出這個bfs序區間,需要知道一個點向左下(右下)走x步到達的點,將樹上每個點和其最左(右)孩子間的邊保留,刪去其它邊,得到一些鏈,維護每個點在鏈上的位置即可支持詢問

求bfs序區間可以做到O(n),bfs由於用到並查集需要O(nlogn)或O(nα(n)),I/O為瓶頸需要O(nlogn)時間

#include<cstdio>
const int N=1e6+77;
int mem[N],*mp=mem;
int n,*e[N][2],v[N],fa[N];
int f[N],q[N],ql=0,qr=0;
int lq[N],rq[N],bq[N];
int id[N][2],bid[N],lp=0,rp=0,F[N];
int gf(int x){
    while(x!=F[x])x=F[x]=F[F[x]];
    return x;
}
void chk(int w,int d){
    if(f[w]>=0)return;
    f[w]
=d; q[++qr]=w; if(e[w][0]==e[w][1])printf("%d\n",d); } void pre(){ ql=qr=0; bq[bid[1]=++qr]=1; while(ql!=qr){ int w=bq[++ql]; if(!id[w][0])for(int x=w;;x=e[x][0][0]){ lq[id[x][0]=++lp]=x; if(e[x][0]==e[x][1])break; } if
(!id[w][1])for(int x=w;;x=e[x][1][-1]){ rq[id[x][1]=++rp]=x; if(e[x][0]==e[x][1])break; } for(int*a=e[w][0],*b=e[w][1];a!=b;bq[bid[*a]=++qr]=*a,++a); } } int _(){ int x; scanf("%d",&x); return x; } int main(){ n=_(); for(int i=1;i<=n+1;++i)F[i]=i; for(int i=1;i<=n;++i)f[i]=-1; for(int w=1,c;w<=n;++w){ v[w]=_(); c=_(); for(int j=0;j<c;++j)fa[mp[j]=_()]=w; e[w][0]=mp; mp+=c; e[w][1]=mp; } pre(); ql=qr=0; chk(1,0); while(ql!=qr){ int w=q[++ql],u,d=f[w]+1; for(int*a=e[w][0],*b=e[w][1];a!=b;++a){ u=*a; if(w!=1)chk(u,d); for(int L=gf(bid[lq[id[u][0]+v[u]]]),R=bid[rq[id[u][1]+v[u]]];L<=R;chk(bq[L],d),L=F[L]=gf(L+1)); } if(w!=1)chk(fa[w],d); } return 0; }

bzoj2035: [2009國家集訓隊]數據讀取問題