luogu P5210 [ZJOI2017]线段树
题面传送门
这个题之所以能黑不是因为它难想而是难写吧……
这个东西名字叫广义线段树那看上去和线段树应该没啥大区别除了时间复杂度不大对之外。
考虑普通线段树的复杂度证明,就是一定有一个节点将这个区间从中间断开,然后两边分别是单侧递归的。
设\(X\)为\(l-1\)在线段树上的叶子节点,\(Y\)为\(r+1\)在线段树上的叶子节点,\(Z\)为\(X,Y\)的LCA,那么\([l,r]\)在线段树上的节点显然是\(X\)到\(Z\)链上不在链上的右节点,与\(Y\)到\(Z\)链上不在链上的左节点。
一开始以为相邻点之间深度加一然而好像不是这样。
正确姿势是维护每个点到根节点链上不在链上的左/右节点深度和个数,然后对\(u\)讨论。
如果\(u\)在\(Z\)子树外或者就是\(Z\),那么这些节点的LCA显然是\(Z\)与\(u\)的LCA,直接计算即可。
如果\(u\)在\(Z\)左子树,那么求出\(u\)与\(X\)的LCA,令其为\(P\),不难发现\(X\)到\(P\)一段的LCA都是\(P\),上面的都是它们祖先。
注意特判\(u\)在\(P\)右子树的情况,因为这样对上去的点会被多算,还要特判右子树为空。
\(z\)在左子树同理。然后就得到一个大常数\(O(n\log n)\)做法。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (800000+5)
#define M (40+5)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int x,y,z,N1,N2,d[N],So[N][2],cnt,Ro,n,m,Bg[N],En[N],Id[N],lg[N],fa[N][20];ll ToT,D[N][2],S[N][2];
I int BD(int l,int r){++cnt;if(l==r)return Id[l]=cnt;int x,Tp=cnt;scanf("%d",&x);So[Tp][0]=BD(l,x);So[Tp][1]=BD(x+1,r);return Tp;}
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(RI i=lg[d[x]];~i;i--) fa[x][i]^fa[y][i]&&(x=fa[x][i],y=fa[y][i]);return fa[x][0];}
I void dfs1(int x,int La){if(!x) return;Bg[x]=++cnt;d[x]=d[La]+1;fa[x][0]=La;for(RI i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];dfs1(So[x][0],x);dfs1(So[x][1],x);En[x]=cnt;}
I void dfs2(int x){for(RI i=1;~i;i--) So[x][i]&&(D[So[x][i]][i]=D[x][i],D[So[x][i]][i^1]=D[x][i^1]+d[So[x][i^1]],S[So[x][i]][i]=S[x][i],S[So[x][i]][i^1]=S[x][i^1]+1,dfs2(So[x][i]),0);}
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
RI i,j;scanf("%d",&n);Ro=BD(1,n);So[cnt+2][0]=Id[0]=cnt+1;So[cnt+2][1]=Ro;So[cnt+4][0]=cnt+2;So[cnt+4][1]=Id[n+1]=cnt+3;Ro=cnt+4;
cnt=0;dfs1(Ro,0);dfs2(Ro);for(i=2;i<=2*n-1;i++) lg[i]=lg[i/2]+1;
scanf("%d",&m);for(i=1;i<=m;i++){scanf("%d%d%d",&z,&x,&y);N1=LCA(x=Id[x-1],y=Id[y+1]);
if(Bg[z]<=Bg[N1]||Bg[z]>En[N1]) N2=LCA(z,N1),ToT=D[x][1]+D[y][0]-D[So[N1][0]][1]-D[So[N1][1]][0]+(d[z]-2*d[N2])*(S[x][1]+S[y][0]-S[So[N1][1]][0]-S[So[N1][0]][1]);
else if(Bg[z]>=Bg[So[N1][1]]) N2=LCA(z,y),ToT=D[x][1]-D[So[N1][0]][1]+(d[z]-2*d[N1])*(S[x][1]-S[So[N1][0]][1])+D[y][0]-D[N2][0]+(d[z]-2*d[N2])*(S[y][0]-S[N2][0])+(d[z]+2)*(S[N2][0]-S[So[N1][1]][0])-(D[N2][0]-D[So[N1][1]][0]),En[z]<=En[So[N2][0]]&&Bg[z]>=Bg[So[N2][0]]&&(ToT-=2);
else N2=LCA(x,z),ToT=D[y][0]-D[So[N1][1]][0]+(d[z]-2*d[N1])*(S[y][0]-S[So[N1][1]][0])+D[x][1]-D[N2][1]+(d[z]-2*d[N2])*(S[x][1]-S[N2][1])+(d[z]+2)*(S[N2][1]-S[So[N1][0]][1])-(D[N2][1]-D[So[N1][0]][1]),Bg[z]>=Bg[So[N2][1]]&&Bg[z]<=En[So[N2][1]]&&(ToT-=2);
printf("%lld\n",ToT),ToT=0;
}
}