luogu P7518 [省选联考 2021 A/B 卷] 宝石
题面传送门
不是很理解陈指导为什么没切掉这道题。
把这个东西分成两端处理,即\([x,lca]\)与\([lca,y]\),因为收集器中宝石种类不一样所以这个\(lca\)被算重了是没有关系的。
前面一个我们可以预处理出对于每一个节点它最近的后一个在什么地方,然后对于每个点找到最近的第一个然后在树上倍增即可。这一部分是\(O(nlogn)\)的。
后面那一段考虑参照前面那一段把序列反过来建倍增数组。
然而你需要确定右端点在哪里才行。
这个东西显然有单调性所以就可以二分右端点,然后用dfs找到树上第一个是\(mid\)的节点并倍增上去。
但是如果对于每次二分都用dfs找到这个点的话那么时间复杂度会爆炸。
因为没有修改,所以可以用整体二分。时间复杂度就降至\(O(nlogn+qlognlogm)\),因为倍增数组每个不可能都指到父节点所以那个\(logn\)其实是跑不满的。
代码有一点点难写。
code:
#include<cstdio>
#include<cstring>
#include<vector>
#define N 200039
#define M 50039
#define I inline
using namespace std;
int n,m,k,q,x,y,z,fa[N][20],lg[N],d[N],ans[N],id[N],C,p[M],f[M],w[N],flag;
struct ques{int st,en,lca,l,r,mid;}g[N];
struct yyy{int to,z;};
struct vi{int id,w;};
vector<vi> c[N];
struct ljb{
int head,h[N];yyy f[2*N];
I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}
}s;
I void dfs1(int x,int last){
yyy tmp;d[x]=d[last]+1;fa[x][0]=last;
for (int i=1;i<=lg[d[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int cur=s.h[x];cur;cur=tmp.z) tmp=s.f[cur],tmp.to^last&&(dfs1(tmp.to,x),0);
}
I void swap(int &x,int &y){x^=y^=x^=y;}
I int lca(int x,int y){
d[x]<d[y]&&(swap(x,y),0);while(d[x]^d[y]) x=fa[x][lg[d[x]-d[y]]];if(x==y) return x;
for(int i=lg[d[x]];~i;i--) if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];return fa[x][0];
}
I void dfs2(int x,int last){
int now=f[w[x]];yyy tmp;d[x]=d[last]+1;fa[x][0]=f[w[x]+1];f[w[x]]=x;
for(int i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int cur=s.h[x];cur;cur=tmp.z)
tmp=s.f[cur],(tmp.to^last)&&(dfs2(tmp.to,x),0);f[w[x]]=now;
}
I int find(int x,int lca){
if(d[x]<d[lca]) return 0;int ans=1;for(int i=lg[d[x]];~i;i--) if(d[fa[x][i]]>=d[lca]) x=fa[x][i],ans+=(1<<i);
return ans;
}
I void dfs3(int x,int last){
int now=f[w[x]];yyy tmp;d[x]=d[last]+1;fa[x][0]=(w[x]?f[w[x]-1]:0);f[w[x]]=x;
for(int i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int cur=s.h[x];cur;cur=tmp.z) tmp=s.f[cur],tmp.to^last&&(dfs3(tmp.to,x),0);f[w[x]]=now;
}
I void dfs4(int x,int last){
int now=f[w[x]];f[w[x]]=x;vi tmp;yyy tmps;for(int i=0;i<c[x].size();i++)tmp=c[x][i],id[tmp.id]=f[tmp.w];
for(int cur=s.
h[x];cur;cur=tmps.z) tmps=s.f[cur],tmps.to^last&&(dfs4(tmps.to,x),0);f[w[x]]=now;
}
I void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
register int i;scanf("%d%d%d",&n,&m,&C);for(i=2;i<=n;i++) lg[i]=lg[i/2]+1;
for(i=1;i<=C;i++) read(p[i]),f[p[i]]=i;
for(i=1;i<=n;i++) read(w[i]),w[i]=f[w[i]];memset(f,0,sizeof(f));
for(i=1;i<n;i++) read(x),read(y),s.add(x,y),s.add(y,x);dfs1(1,0);
scanf("%d",&q);for(i=1;i<=q;i++) read(g[i].st),read(g[i].en),g[i].lca=lca(g[i].st,g[i].en);
for(i=1;i<=q;i++) c[g[i].st].push_back((vi){i,1});dfs4(1,0);
memset(fa,0,sizeof(fa));dfs2(1,0);for(i=1;i<=q;i++)ans[i]=find(id[i],g[i].lca);
memset(fa,0,sizeof(fa));dfs3(1,0);for(i=1;i<=q;i++) g[i].l=0,g[i].r=C-ans[i]+1;
while(1){
for(i=1;i<=n;i++) c[i].clear();flag=0;
for(i=1;i<=q;i++){
if(g[i].l+1<g[i].r){
g[i].mid=g[i].l+g[i].r>>1;
c[g[i].en].push_back((vi){i,ans[i]+g[i].mid});flag=1;
}
}
if(!flag) break; dfs4(1,0);
for(i=1;i<=q;i++) g[i].l+1<g[i].r&&((g[i].mid>find(id[i],g[i].lca)?g[i].r:g[i].l)=g[i].mid);
}
for(i=1;i<=q;i++)printf("%d\n",g[i].l+ans[i]);
}