知识点:主席树,(LCA)
题目大意:给定树上两点 (x) ,(y) ,求 (x) 到 (y) 的简单路径上权值在 (l) , (r) 之间的数的和
看到题目,首先可以想到权值主席树,将每个节点至根节点这一条链上的节点信息存下来
对于节点 (u) ,可以从他的父亲节点处更新
然后用 (LCA) 解决
要点:
1.权值需要离散化
2.主席树码量较大,调试需要耐心
- (LCA) 最好用 (tarjan) 求,不要用倍增,不然容易 (MLE) (不要问我怎么知道的)
4.记得开 (long) (long) !
代码 ( (tarjan) (LCA) 版):
#include<bits/stdc++.h>
namespace my_std
{
using namespace std;
#define int long long
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define drep(i,a,b) for (int i=(a);i>=(b);i--)
#define go(u) for (int i=head[(u)];i;i=e[i].nxt)
#define pf printf
#define writeln(x) write(x),putchar('
')
#define writesp(x) write(x),putchar(' ')
#define mem(x,v) memset(x,v,sizeof(x))
typedef long long ll;
const int INF=0x7fffffff;
inline int read()
{
int sum=0,f=1;
char c=0;
while (!isdigit(c))
{
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c))
{
sum=(sum<<1)+(sum<<3)+(c^48);
c=getchar();
}
return sum*f;
}
void write(int k)
{
if (k<0) putchar('-'),k=-k;
if (k>=10) write(k/10);
putchar(k%10+'0');
}
}
using namespace my_std;
const int N=100010;
struct Tree
{
struct point
{
int l,r,val;
}tree[N*18];
int cnt=0,T[N];
inline void push_up(int node)
{
tree[node].val=tree[tree[node].l].val+tree[tree[node].r].val;
}
inline int clone(int node)
{
tree[++cnt]=tree[node];
return cnt;
}
void build(int &node,int start,int end)
{
node=++cnt;
if (start==end) return;
int mid=(start+end)>>1;
build(tree[node].l,start,mid);
build(tree[node].r,mid+1,end);
}
void update(int &node,int last,int start,int end,int p,int val)
{
node=clone(last);
tree[node].val+=val;
if (start==end) return;
int mid=(start+end)>>1;
if (p<=mid) update(tree[node].l,tree[last].l,start,mid,p,val);
else update(tree[node].r,tree[last].r,mid+1,end,p,val);
}
int query(int node,int start,int end,int l,int r)
{
if (start>r||end<l) return 0;
if (start>=l&&end<=r) return tree[node].val;
int mid=(start+end)>>1;
int sum_left=query(tree[node].l,start,mid,l,r);
int sum_right=query(tree[node].r,mid+1,end,l,r);
return sum_left+sum_right;
}
}tree;
struct edge
{
int to,nxt;
}e[N<<1];
struct qedge
{
int to,nxt,lca;
}qe[N<<1];
int n,m,t,cnt,qcnt=1,head[N],qhead[N],fa[N];
int a[N],val[N];
inline void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
inline void addq(int u,int v)
{
qe[++qcnt].to=v;
qe[qcnt].nxt=qhead[u];
qhead[u]=qcnt;
}
struct Query
{
int u,v,l,r;
void get()
{
u=read(),v=read(),l=read(),r=read();
addq(u,v),addq(v,u);
}
}p[N];
bool vis[N];
int f[N];
inline int getfa(int x)
{
if (fa[x]==x) return x;
return fa[x]=getfa(fa[x]);
}
void tarjan(int u)
{
vis[u]=1;
fa[u]=u;
go(u)
{
int v=e[i].to;
if (vis[v])
{
f[u]=v;
continue;
}
tarjan(v);
fa[v]=u;
}
for (int i=qhead[u];i;i=qe[i].nxt)
{
int v=qe[i].to;
if (vis[v])
{
qe[i].lca=getfa(v);
qe[i^1].lca=qe[i].lca;
}
}
}
void get_tree(int u,int fa)
{
tree.update(tree.T[u],tree.T[fa],1,t,a[u],val[a[u]]);
go(u)
{
int v=e[i].to;
if (v==fa) continue;
get_tree(v,u);
}
}
inline void solve(int x,int y,int LCA,int l,int r)
{
int tmp1=tree.query(tree.T[x],1,t,l,r);
int tmp2=tree.query(tree.T[y],1,t,l,r);
int tmp3=tree.query(tree.T[LCA],1,t,l,r);
int tmp4=tree.query(tree.T[f[LCA]],1,t,l,r);
writesp(tmp1+tmp2-tmp3-tmp4);
}
signed main()
{
n=read();m=read();
rep(i,1,n) a[i]=val[i]=read();
rep(i,1,n-1)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
sort(val+1,val+n+1);
t=unique(val+1,val+n+1)-val-1;
rep(i,1,n) a[i]=upper_bound(val+1,val+t+1,a[i])-val-1;
get_tree(1,0);
rep(i,1,m) p[i].get();
tarjan(1);
rep(i,1,m)
{
int u=p[i].u,v=p[i].v,l=p[i].l,r=p[i].r;
int lca=qe[i<<1].lca;
l=lower_bound(val+1,val+t+1,l)-val;
r=upper_bound(val+1,val+t+1,r)-val-1;
if (l>r)
{
writesp(0);
continue;
}
solve(u,v,lca,l,r);
}
}
代码(倍增 (LCA) 版):
#include<bits/stdc++.h>
namespace my_std
{
using namespace std;
#define int long long
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define drep(i,a,b) for (int i=(a);i>=(b);i--)
#define go(u) for (int i=head[(u)];i;i=e[i].nxt)
#define pf printf
#define writeln(x) write(x),putchar('
')
#define writesp(x) write(x),putchar(' ')
#define mem(x,v) memset(x,v,sizeof(x))
typedef long long ll;
const int INF=0x7fffffff;
inline int read()
{
int sum=0,f=1;
char c=0;
while (!isdigit(c))
{
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c))
{
sum=(sum<<1)+(sum<<3)+(c^48);
c=getchar();
}
return sum*f;
}
void write(int k)
{
if (k<0) putchar('-'),k=-k;
if (k>=10) write(k/10);
putchar(k%10+'0');
}
}
using namespace my_std;
const int N=100010;
struct Irelia
{
int id,val;
}p[N];
struct Tree
{
struct point
{
int l,r,val;
}tree[N<<5];
int cnt=0,T[N];
int clone(int node)
{
tree[++cnt]=tree[node];
return cnt;
}
void update(int &node,int last,int start,int end,int p,int val)
{
node=clone(last);
tree[node].val+=val;
if (start==end) return;
int mid=(start+end)>>1;
if (p<=mid) update(tree[node].l,tree[last].l,start,mid,p,val);
else update(tree[node].r,tree[last].r,mid+1,end,p,val);
}
int query(int node,int start,int end,int l,int r)
{
if (start>r||end<l) return 0;
if (start>=l&&end<=r) return tree[node].val;
int mid=(start+end)>>1;
int sum_left=query(tree[node].l,start,mid,l,r);
int sum_right=query(tree[node].r,mid+1,end,l,r);
return sum_left+sum_right;
}
}tree;
struct edge
{
int to,nxt;
}e[N<<1];
int n,m,cnt,head[N],f[N][21],dep[N];
int a[N],val[N],ln;
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs(int u,int fa)
{
dep[u]=dep[fa]+1;
f[u][0]=fa;
for (int i=1;(1<<i)<=dep[u];i++)
{
f[u][i]=f[f[u][i-1]][i-1];
}
go(u)
{
int v=e[i].to;
if (v==fa) continue;
dfs(v,u);
}
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
drep(i,ln,0)
{
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
}
drep(i,ln,0)
{
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
void get_tree(int u,int fa)
{
tree.update(tree.T[u],tree.T[fa],1,n,a[u],val[a[u]]);
go(u)
{
int v=e[i].to;
if (v==fa) continue;
get_tree(v,u);
}
}
void solve(int x,int y,int l,int r)
{
int LCA=lca(x,y);
int ans=a[LCA]>=l&&a[LCA]<=r?val[a[LCA]]:0;
int tmp1=tree.query(tree.T[x],1,n,l,r);
int tmp2=tree.query(tree.T[y],1,n,l,r);
int tmp3=tree.query(tree.T[LCA],1,n,l,r);
writesp(tmp1+tmp2-(tmp3<<1)+ans);
}
signed main()
{
n=read();m=read();
ln=log2(n);
rep(i,1,n) a[i]=val[i]=read();
rep(i,1,n-1)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
sort(val+1,val+n+1);
int t=unique(val+1,val+n+1)-val-1;
rep(i,1,n) a[i]=upper_bound(val+1,val+t+1,a[i])-val-1;
dfs(1,0);get_tree(1,0);
rep(i,1,m)
{
int u=read(),v=read(),l=read(),r=read();
if (l>r)
{
writesp(0);
continue;
}
l=lower_bound(val+1,val+t+1,l)-val;
r=upper_bound(val+1,val+t+1,r)-val-1;
solve(u,v,l,r);
}
}
备注:听 (Frez) 学长说因为此题离线,还可以将查询分成 (0) 至 (r) , (0) 至 (l-1) 两部分,可以用线段树实现
我太菜了.jpg