专栏文章
P11364 [NOIP2024] 树上查询
P11364题解参与者 7已保存评论 9
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 9 条
- 当前快照
- 1 份
- 快照标识符
- @miqxqaw6
- 此快照首次捕获于
- 2025/12/04 12:27 3 个月前
- 此快照最后确认于
- 2025/12/04 12:27 3 个月前
纪念这场把我创飞的 NOIP,为什么考场上写那么久还写不出来呢?
baka。
做过 P7880 应该很容易想到离线然后使用树上启发式合并预处理。设当前遍历到了点 ,对于所有 轻子树中的结点 ,我们希望知道两个 使得 ,且 中的点都在 子树内,当然 要尽量小, 要尽量大。于是 的贡献就可以写成 这样的形势,对于一组询问 ,如果 的大小 ,那么就有 的贡献。
如何找出所有的 ?考虑二分,我们需要知道一段区间内的数的 dfs 序的最大和最小值,以便判断这些数是否都在 子树内,st 表预处理即可。
然后处理询问,通过分讨交在左边,交在右边,以及包含的情况,不难发现限制是一个二维偏序,简单处理即可。
这样做时间复杂度是 ,不够优秀,尝试优化寻找三元组的部分。发现本质不同的 连续段实际上只有 个,因为它一定是由若干个小的连续段拼起来的。考虑用并查集代替二分,记 和 表示 所在的连续段的左右端点,合并两段时并查集维护即可。
不算并查集的话复杂度 ,本地拍了几千组小数据,极限数据 2.5s。瓶颈在二维偏序,启发式合并只花了 0.6s,应该是线段树太慢了,被卡常再改吧。
upd:云斗上过了,应该没事。
CPP#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <ctime>
#include <cstdio>
#include <random>
#include <vector>
#include <bitset>
#include <cassert>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define MISAKA main
#define ll long long
#define eb emplace_back
#define pii pair<int,int>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _rep(i,a,b) for(int i=(a);i>=(b);--i)
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define FIO(FILE) freopen(FILE".in","r",stdin),freopen(FILE".out","w",stdout)
using namespace std;
bool __st;
inline int read(){
char ch=getchar();int f=1,x=0;
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e5+10,mod=998244353;
int n,q,dfn[N],id[N],siz[N],tim,son[N],dep[N],ans[N],nxt[N],pre[N];
vector<int> g[N];set<int> s;
struct node{int x,y,k,id;
node(int a=0,int b=0,int c=0,int d=0){x=a,y=b,k=c,id=d;}
};vector<node> v1,v2;
bool cmp(node a,node b){return a.k>b.k;}
vector<pii> Q[N],v[N];
int gn(int x){return nxt[x]=x==nxt[x]?x:gn(nxt[x]);}
int gp(int x){return pre[x]=x==pre[x]?x:gp(pre[x]);}
void solve(int x,int L,int R,int d){
int p=gp(x),q=gn(x),flag=0;
while(L<=dfn[q+1]&&dfn[q+1]<=R) nxt[q]=q+1,pre[q+1]=q,q=gn(q+1),flag=1;
while(L<=dfn[p-1]&&dfn[p-1]<=R) pre[p]=p-1,nxt[p-1]=p,p=gp(p-1),flag=1;
if(flag) v1.eb(p,q,q-p+1,d),v[q].eb(p,d);
}
void dfs(int u,int f){
dep[u]=dep[f]+1,siz[u]=1;id[dfn[u]=++tim]=u;
for(int v:g[u])if(v!=f){
dfs(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs(int u,int f,int tp){
for(int v:g[u])if(v!=f&&v!=son[u]) dfs(v,u,0);
if(son[u]) dfs(son[u],u,1);
for(int v:g[u])if(v!=f&&v!=son[u])
rep(i,dfn[v],dfn[v]+siz[v]-1)
solve(id[i],dfn[u],dfn[u]+siz[u]-1,dep[u]);
solve(u,dfn[u],dfn[u]+siz[u]-1,dep[u]);
}
struct segtree{int l,r,mx;}t[N<<2];
void bd(int x,int l,int r){
t[x]={l,r,0};int mid=l+r>>1;
if(l^r) bd(2*x,l,mid),bd(2*x+1,mid+1,r);
}
void upd(int x,int p,int k){
t[x].mx=max(t[x].mx,k);
if(t[x].l^t[x].r) upd(2*x+(p>t[2*x].r),p,k);
}
int qry(int x,int l,int r){
if(t[x].r<l||t[x].l>r) return 0;
if(l<=t[x].l&&t[x].r<=r) return t[x].mx;
return max(qry(2*x,l,r),qry(2*x+1,l,r));
}
void misaka(){
n=read();
rep(i,2,n){
int u=read(),v=read();
g[u].eb(v);g[v].eb(u);
}
dfs(1,0);
rep(i,1,n) pre[i]=i,nxt[i]=i,v1.eb(i,i,1,dep[i]);
dfs(1,0,1);q=read();
rep(i,1,q){
int l=read(),r=read(),k=read();
Q[r].eb(l,i);v2.eb(l,r,k,i);
}
sort(v1.begin(),v1.end(),cmp);
sort(v2.begin(),v2.end(),cmp);
bd(1,1,n);
int j=0;rep(i,0,v2.size()-1){
while(j<v1.size()&&v1[j].k>=v2[i].k)
upd(1,v1[j].y,v1[j].id),j++;
int id=v2[i].id,l=v2[i].x+v2[i].k-1,r=v2[i].y;
ans[id]=max(ans[id],qry(1,l,r));
}
bd(1,1,n);
j=0;rep(i,0,v2.size()-1){
while(j<v1.size()&&v1[j].k>=v2[i].k)
upd(1,v1[j].x,v1[j].id),j++;
int id=v2[i].id,l=v2[i].x,r=v2[i].y-v2[i].k+1;
ans[id]=max(ans[id],qry(1,l,r));
}
bd(1,1,n);
_rep(i,n,1){
for(auto [x,y]:v[i]) upd(1,x,y);
for(auto [x,y]:Q[i]) ans[y]=max(ans[y],qry(1,1,x));
}
rep(i,1,q) printf("%d\n",ans[i]);
}
bool __ed;
signed MISAKA(){
#ifdef LOCAL_MSK
atexit([](){
debug("\n%.3lfs ",(double)clock()/CLOCKS_PER_SEC);
debug("%.3lfMB",abs(&__st-&__ed)/1024./1024);});
#endif
int T=1;
while(T--) misaka();
return 0;
}
相关推荐
评论
共 9 条评论,欢迎与作者交流。
正在加载评论...