题面
此题树上不带修,查询路径信息,时限较长,可以树上莫队。利用树上莫队的传统方法,先树分块,对询问排序,然后再执行类似普通莫队的操作即可。但这里的修改操作并不好处理,于是在外层套一分块。每次修改就先修改单点的奇偶性,再修改块内奇数的总数。查询时用普通分块查询方法即可。
时间复杂度
O
(
(
n
+
q
)
n
)
O((n+q) \sqrt{n})
O((n+q)n),空间复杂度
O
(
n
+
q
)
O(n+q)
O(n+q)
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
#define R register int
#define L long long
#define I inline
#define N 300001
#define S 550
vector<int>G[N];
int a[N],pos[600001],b[546],dep[N],in[N],out[N],ans[N],h[N],ct[N],f[N],Top[N],sz[N];
bool tag[N],vc[N];
struct Query{
int lf,rt,Start,End,id,Ql,Qr;
I friend bool operator<(Query x,Query y){
if(x.lf/S!=y.lf/S){
return x.lf<y.lf;
}
return x.rt<y.rt;
}
}q[N];
I void PreDFS(R x,R F,int&ct){
ct++;
pos[ct]=x;
sz[x]=1;
in[x]=ct;
dep[x]=dep[F]+1;
f[x]=F;
for(vector<int>::iterator T=G[x].begin();T!=G[x].end();T++){
if(*T!=F){
PreDFS(*T,x,ct);
sz[x]+=sz[*T];
if(sz[*T]>sz[h[x]]){
h[x]=*T;
}
}
}
ct++;
out[x]=ct;
pos[ct]=x;
}
I void ReDFS(R x,R t){
Top[x]=t;
if(h[x]!=0){
ReDFS(h[x],t);
for(vector<int>::iterator T=G[x].begin();T!=G[x].end();T++){
if(*T!=f[x]&&*T!=h[x]){
ReDFS(*T,*T);
}
}
}
}
I int LCA(R x,R y){
while(Top[x]!=Top[y]){
if(dep[Top[x]]>dep[Top[y]]){
x=f[Top[x]];
}else{
y=f[Top[y]];
}
}
return dep[x]<dep[y]?x:y;
}
I void Add(int x){
vc[x]^=true;
if(vc[x]==true){
b[x/S]++;
}else{
b[x/S]--;
}
}
I int GetAns(int l,int r){
int bl=l/S,br=r/S;
if(bl==br){
for(R i=l;i<=r;i++){
if(vc[i]==true){
return i;
}
}
return-1;
}
for(R i=bl+1;i!=br;i++){
if(b[i]!=0){
for(R j=i*S;j!=(i+1)*S;j++){
if(vc[j]==true){
return j;
}
}
}
}
for(R i=l;i!=(bl+1)*S;i++){
if(vc[i]==true){
return i;
}
}
for(R i=br*S;i<=r;i++){
if(vc[i]==true){
return i;
}
}
return-1;
}
int main(){
int n,m,x,y;
scanf("%d%d",&n,&m);
for(R i=1;i<=n;i++){
scanf("%d",a+i);
}
for(R i=1;i!=n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
x=0;
PreDFS(1,0,x);
ReDFS(1,1);
for(R i=0;i!=m;i++){
scanf("%d%d%d%d",&q[i].Start,&q[i].End,&q[i].Ql,&q[i].Qr);
R st=q[i].Start,ed=q[i].End;
R t=LCA(st,ed);
q[i].id=i;
if(t==st){
q[i].lf=in[t];
q[i].rt=in[ed];
}else if(t==ed){
q[i].lf=in[t];
q[i].rt=in[st];
}else if(out[st]<in[ed]){
q[i].lf=out[st];
q[i].rt=in[ed];
}else{
q[i].lf=out[ed];
q[i].rt=in[st];
}
}
sort(q,q+m);
R l=1,r=0;
for(R i=0;i!=m;i++){
R tem,curr=q[i].rt,curl=q[i].lf;
while(r<curr){
r++;
tem=pos[r];
Add(a[tem]);
tag[tem]^=true;
}
while(l>curl){
l--;
tem=pos[l];
Add(a[tem]);
tag[tem]^=true;
}
while(r>curr){
tem=pos[r];
Add(a[tem]);
tag[tem]^=true;
r--;
}
while(l<curl){
tem=pos[l];
Add(a[tem]);
tag[tem]^=true;
l++;
}
R lca=LCA(q[i].Start,q[i].End);
if(lca!=q[i].Start&&q[i].End!=lca){
Add(a[lca]);
ans[q[i].id]=GetAns(q[i].Ql,q[i].Qr);
Add(a[lca]);
}else{
ans[q[i].id]=GetAns(q[i].Ql,q[i].Qr);
}
}
for(R i=0;i!=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
莫队虽然勉强可过,但效率不高。此题可以采用随机化的算法。给每种矿物一个随机标签,若路径上属于给定区间的标签异或和不为
0
0
0,则一定有个数为奇数的矿物;若异或和为
0
0
0,则只有极小的概率存在个数为奇数的矿物。于是对原树建值域主席树,每次询问查询区间异或和即可。
时间复杂度
O
(
(
n
+
q
)
log
2
n
)
O((n+q) \log_2n)
O((n+q)log2n),空间复杂度
O
(
n
log
2
n
)
O(n \log_2n)
O(nlog2n)
#include<stdio.h>
#include<vector>
#include<algorithm>
#include<time.h>
using namespace std;
#define R register int
#define L long long
#define I inline
#define N 300001
struct SegmentNode{
int Ls,Rs;
L XorSum;
}t[9999999];
int a[N],f[N],h[N],sz[N],dep[N],Top[N],ct;
I void GetNode(int&x){
ct++;
x=ct;
}
L v[N];
I void Insert(int p1,int p2,int lf,int rt,const int x){
t[p2].XorSum=v[x]^t[p1].XorSum;
if(lf!=rt){
int mid=lf+rt>>1;
if(x>mid){
GetNode(t[p2].Rs);
t[p2].Ls=t[p1].Ls;
Insert(t[p1].Rs,t[p2].Rs,mid+1,rt,x);
}else{
GetNode(t[p2].Ls);
t[p2].Rs=t[p1].Rs;
Insert(t[p1].Ls,t[p2].Ls,lf,mid,x);
}
}
}
I int GetAns(int p1,int p2,int p3,int p4,int lf,int rt,const int l,const int r){
if(l<=lf&&rt<=r&&(t[p1].XorSum^t[p2].XorSum^t[p3].XorSum^t[p4].XorSum)==0){
return-1;
}
if(lf==rt){
return rt;
}
int mid=lf+rt>>1,q=-1;
if(l<=mid){
q=GetAns(t[p1].Ls,t[p2].Ls,t[p3].Ls,t[p4].Ls,lf,mid,l,r);
}
if(q==-1&&r>mid){
return GetAns(t[p1].Rs,t[p2].Rs,t[p3].Rs,t[p4].Rs,mid+1,rt,l,r);
}
return q;
}
I L GetRand(){
return(L)rand()<<45|(L)rand()<<30|rand()<<15;
}
vector<int>G[N];
I void PreDFS(int x,int F,int&n){
dep[x]=dep[F]+1;
f[x]=F;
sz[x]=1;
Insert(F,x,1,n,a[x]);
for(vector<int>::iterator T=G[x].begin();T!=G[x].end();T++){
if(*T!=F){
PreDFS(*T,x,n);
sz[x]+=sz[*T];
if(sz[*T]>sz[h[x]]){
h[x]=*T;
}
}
}
}
I void ReDFS(int x,int t){
Top[x]=t;
if(h[x]!=0){
ReDFS(h[x],t);
for(vector<int>::iterator T=G[x].begin();T!=G[x].end();T++){
if(*T!=f[x]&&*T!=h[x]){
ReDFS(*T,*T);
}
}
}
}
I int GetLCA(int x,int y){
while(Top[x]!=Top[y]){
if(dep[Top[x]]>dep[Top[y]]){
x=f[Top[x]];
}else{
y=f[Top[y]];
}
}
return dep[x]<dep[y]?x:y;
}
int main(){
int n,q,x,y,l,r,z;
scanf("%d%d",&n,&q);
ct=n;
srand((int)time(0));
for(R i=1;i<=n;i++){
scanf("%d",a+i);
v[i]=GetRand();
}
for(R i=1;i!=n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
PreDFS(1,0,n);
ReDFS(1,1);
for(R i=0;i!=q;i++){
scanf("%d%d%d%d",&x,&y,&l,&r);
z=GetLCA(x,y);
printf("%d\n",GetAns(x,y,z,f[z],1,n,l,r));
}
return 0;
}

本文介绍了如何通过树上莫队算法优化查询路径信息,同时处理修改操作的困难,外层套分块策略。另一种解决方案是采用随机化方法,利用矿物标签的异或特性判断路径中奇数矿物的存在概率,结合值域主席树实现高效查询。两种方法的时间复杂度和空间复杂度对比鲜明。
480

被折叠的 条评论
为什么被折叠?



