HDU - 5840 - This world need more Zhu
将询问分为
k
大和
对于
对于向上的边,开始点
(d[x]−d[u]+1)%k==0
。故
d[x]=d[u]−k+1
。
对于向下的边,开始点
(d[x]−d[lca]+1+d[u]−d[lca])%k==0
,
d[x]=d[lca]+(d[lca]−d[u]−1)%k
。
可以直接从开始点向上/向下走,每次询问复杂度为
O(nB)
。
对于
k
小的情况,我们考虑使用树剖。
对于一条链
对于向上的边
x
被计数当且仅当
对于向下的边
x
被计数当且仅当
对于每一个
k
。可以考虑将树上所有点根据第一关键字 dfs
序升序来排序,放到线段树上。这样对于一个询问,我们只需要访问一条链上模
k
值给定的一段就行。对于每条重链,要访问的一段应该是连续的,因为重链的 dfs
序连续。一次询问复杂度是
因此总的复杂度是
O(QlargenB+n+Bn+Qsmalllog2n)
。
将
B
取
#include<bits/stdc++.h>
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int N=1e5+7;
const int BLOCK=100;
int n,a[N],ans[N],son[N],fa[N],top[N],sz[N],id[N],d[N],b[N],cnt,st[N],tp,mx[N<<2],L[BLOCK],R[BLOCK],tmp[N];
vector<int> hs[BLOCK],adj[N];
struct Query
{
int u,v,k,down,lca,id;
};
vector<Query> q1[N],q2[BLOCK];
inline int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(d[top[u]]<d[top[v]]) swap(u,v);
u=top[u];
u=fa[u];
}
return d[u]<d[v]?u:v;
}
inline void init()
{
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
top[1]=1;
tp=cnt=0;
memset(son,-1,sizeof(son));
for(int i=1;i<=n;++i) q1[i].clear(),adj[i].clear();
for(int i=0;i<BLOCK;++i) q2[i].clear();
}
void dfs1(int u,int deep,int father)
{
sz[u]=1;d[u]=deep;fa[u]=father;
for(int v : adj[u])
{
if(v==father) continue;
dfs1(v,deep+1,u);
sz[u]+=sz[v];
if(son[u]==-1||sz[v]>sz[son[u]])
son[u]=v;
}
}
void dfs2(int u)
{
id[u]=++cnt;
b[cnt]=u;
if(son[u]!=-1)
{
top[son[u]]=top[u];
dfs2(son[u]);
}
for(int v : adj[u])
{
if(v==fa[u]||v==son[u]) continue;
top[v]=v;
dfs2(v);
}
}
void work_1(int u)
{
st[tp++]=u;
for(Query & q : q1[u])
{
int u=q.u,v=q.v,lca=q.lca,k=q.k,id=q.id,down=q.down;
if(down)
{
int tmp=d[lca]+((d[lca]-d[u]-1)%k+k)%k;
while(tmp<tp&&d[st[tmp]]<=d[v]) ans[id]=max(ans[id],a[st[tmp]]),tmp+=k;
}
else
{
int tmp=tp-k;
while(tmp>=0&&d[st[tmp]]>=d[lca]) ans[id]=max(ans[id],a[st[tmp]]),tmp-=k;
}
}
for(int v : adj[u])
{
if(v==fa[u]) continue;
work_1(v);
}
--tp;
}
inline void push_up(int rt)
{
mx[rt]=max(mx[lson],mx[rson]);
}
void build(int rt,int l,int r)
{
if(l==r) { mx[rt]=a[b[tmp[l]]];return ; }
int m=(l+r)>>1;
build(lson,l,m);
build(rson,m+1,r);
push_up(rt);
}
int query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&qr>=r) return mx[rt];
int m=(l+r)>>1;
int res=0;
if(ql<=m) res=max(res,query(lson,l,m,ql,qr));
if(qr>m) res=max(res,query(rson,m+1,r,ql,qr));
return res;
}
inline int cal(int l,int r,int k)
{
l=lower_bound(tmp+L[k],tmp+R[k]+1,l)-tmp;
r=upper_bound(tmp+L[k],tmp+R[k]+1,r)-tmp-1;
return l<=r?query(1,1,n,l,r):0;
}
inline void solve(int x,int u,int k,int id)
{
if(d[u]<d[x]) return ;
while(top[u]!=top[x])
{
ans[id]=max(ans[id],cal(::id[top[u]],::id[u],k));
u=fa[top[u]];
}
ans[id]=max(ans[id],cal(::id[x],::id[u],k));
}
inline void work_2(int k)
{
for(int i=0;i<k;++i) hs[i].clear();
for(int i=1;i<=n;++i) hs[d[b[i]]%k].push_back(i);
int tot=0;
for(int i=0;i<k;++i)
{
L[i]=tot+1;
for(int v : hs[i]) tmp[++tot]=v;
R[i]=tot;
}
build(1,1,n);
for(Query & q : q2[k])
{
int u=q.u,v=q.v,k=q.k,lca=q.lca,id=q.id;
solve(lca,u,(d[u]+1)%k,id);
solve(lca,v,((2*d[lca]-d[u]-1)%k+k)%k,id);
}
}
int main()
{
int T,m;
scanf("%d",&T);
for(int kase=1;kase<=T;++kase)
{
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs1(1,0,1);
dfs2(1);
for(int i=0;i<m;++i)
{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
if(k<BLOCK) q2[k].push_back({u,v,k,1,LCA(u,v),i});
else
{
q1[u].push_back({u,v,k,0,LCA(u,v),i});
q1[v].push_back({u,v,k,1,LCA(u,v),i});
}
}
work_1(1);
for(int i=0;i<BLOCK;++i) if(q2[i].size()) work_2(i);
printf("Case #%d:\n",kase);
for(int i=0;i<m;++i)
printf("%d\n",ans[i]);
}
return 0;
}