传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4539
思路:首先把大树缩点,一个点代表一次操作复制的子树
两个点之间的边权值就是两个子树的根在大树中的距离,这个可以在原树中用倍增求出
至于从大树标号转成原树标号,就相当于求子树内编号第k大的点的编号,用可持久化线段树即可。
询问的话,就先把两个点移到对应复制操作的子树的根,计算距离,再在缩好点的大树里跳到lca,计算距离,再把lca多算的那段减掉即可。
考场上想到了60分,突然就不会求区间第k大了,然后就放弃了这题...
细节较多,一波大讨论即可。
#include
#include
#include
#include
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]=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