传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4539
思路:首先把大树缩点,一个点代表一次操作复制的子树
两个点之间的边权值就是两个子树的根在大树中的距离,这个可以在原树中用倍增求出
至于从大树标号转成原树标号,就相当于求子树内编号第k大的点的编号,用可持久化线段树即可。
询问的话,就先把两个点移到对应复制操作的子树的根,计算距离,再在缩好点的大树里跳到lca,计算距离,再把lca多算的那段减掉即可。
考场上想到了60分,突然就不会求区间第k大了,然后就放弃了这题...
细节较多,一波大讨论即可。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const ll maxn=100010,maxm=200010,maxk=22,maxt=2200000; using namespace std; int n,m,Q,dfn[maxn],last[maxn],tim,pw[maxk]; int root[maxn],from[maxn],idx;ll cnt[maxn],nn; //root 该点表示子树的根,from该点接在原树哪个点下面,cnt该次操作后新树的大小,nn新树大小 void read(int &x){ char ch; for (ch=getchar();!isdigit(ch);ch=getchar()); for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; } void read(ll &x){ char ch; for (ch=getchar();!isdigit(ch);ch=getchar()); for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; } struct Tsegment{ #define ls ch[p][0] #define rs ch[p][1] #define mid ((l+r)>>1) int siz[maxt],ch[maxt][2],root[maxn],tot; void insert(int pre,int &p,int l,int r,int x){ p=++tot,siz[p]=siz[pre]+1; if (l==r) return; if (x<=mid) rs=ch[pre][1],insert(ch[pre][0],ls,l,mid,x); else ls=ch[pre][0],insert(ch[pre][1],rs,mid+1,r,x); } void insert(int id,int v){insert(root[id-1],root[id],1,n,v);} int query(int pre,int p,int l,int r,int k){ if (l==r){return l;} if (siz[ls]-siz[ch[pre][0]]>=k) return query(ch[pre][0],ls,l,mid,k); else return query(ch[pre][1],rs,mid+1,r,k-(siz[ls]-siz[ch[pre][0]])); } int query(int x,int y,int k){return query(root[x-1],root[y],1,n,k);} }T; struct Tgraph1{ int pre[maxm],now[maxn],son[maxm],tot,dep[maxn],fa[maxn][maxk],siz[maxn]; ll dis[maxn],val[maxm]; void add(int a,int b,ll c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;} void ins(int a,int b,ll c){add(a,b,c),add(b,a,c);} void dfs(int x){ for (int i=1;i<=18;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0]) fa[son[y]][0]=x,dis[son[y]]=dis[x]+val[y],dep[son[y]]=dep[x]+1,dfs(son[y]); } void dfs2(int x){ siz[x]=1,dfn[x]=++tim,T.insert(tim,x); for (ll y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0]) dfs2(son[y]),siz[x]+=siz[son[y]]; last[x]=tim; } void jump(int &x,int h){for (int i=18;i>=0;i--) if (h&pw[i]) x=fa[x][i],h-=pw[i];} int lca(int x,int y){ if (dep[x]<dep[y]) swap(x,y); jump(x,dep[x]-dep[y]); if (x==y) return x; for (int i=18;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } ll dist(int x,int y){return dis[x]+dis[y]-2*dis[lca(x,y)];} int up(int x,int y){jump(x,dep[x]-dep[y]-1);return x;} }ori,g; int getid(ll x){return lower_bound(cnt+1,cnt+1+idx,x)-cnt;} ll query(ll a,ll b){ int ida=getid(a),rta=root[ida],aa=T.query(dfn[rta],last[rta],a-cnt[ida-1]); int idb=getid(b),rtb=root[idb],bb=T.query(dfn[rtb],last[rtb],b-cnt[idb-1]); int lca=g.lca(ida,idb); if (ida==idb) return ori.dist(aa,bb); ll res=g.dist(ida,idb)+ori.dis[aa]-ori.dis[rta]+ori.dis[bb]-ori.dis[rtb]; if (ida==lca){ int frb=from[g.up(idb,lca)]; res-=ori.dis[aa]+ori.dis[frb]-ori.dist(aa,frb)-2*ori.dis[rta]; } else if (idb==lca){ int fra=from[g.up(ida,lca)]; res-=ori.dis[bb]+ori.dis[fra]-ori.dist(bb,fra)-2*ori.dis[rtb]; } else{ int fra=from[g.up(ida,lca)]; int frb=from[g.up(idb,lca)]; res-=ori.dis[fra]+ori.dis[frb]-ori.dist(fra,frb)-2*ori.dis[root[lca]]; } return res; } int main(){ //freopen("tree.in","r",stdin);freopen("tree.out","w",stdout); scanf("%d%d%d",&n,&m,&Q); pw[0]=1;for (int i=1;i<=19;i++) pw[i]=pw[i-1]<<1; for (int i=1,x,y;i<n;i++) read(x),read(y),ori.ins(x,y,1); ori.dfs(1),ori.dfs2(1); nn=n,cnt[1]=nn,root[1]=idx=1; for (int i=2;i<=m+1;i++){ ll x,y; read(x),read(y); int id=getid(y),rt=root[id]; root[i]=x,idx=i,from[i]=T.query(dfn[rt],last[rt],y-cnt[id-1]); g.ins(i,id,ori.dis[from[i]]-ori.dis[rt]+1); nn+=ori.siz[x],cnt[i]=nn; } g.dfs(1); for (int i=1;i<=Q;i++){ ll a,b;read(a),read(b); printf("%lld\n",query(a,b)); } return 0; }