题解[P5573 星际kfc篮球赛]

题目链接

题意:
给定一张完全图,每两个点 \((i,j)\) 之间有三种边 \(A_{(i,j)},B_{(i,j)},C_{(i,j)}\)
其中 \(A_{(i,j)}=a_i\operatorname{xor} a_j,B{(i,j)}=b_i\operatorname{xor} b_j,C_{(i,j)}=c_i\operatorname{xor} c_j\)
每次查询对三种边分别给出一个起点与阀值,
问从三种图中分别从各自起点开始,经过不大于各自阀值的边到达的点的交集。

这道题几乎是强行拼题。

首先对每张图求出各自的 \(\text{Kruskal}\) 重构树,就是这题

从大到小枚举每一位,将这一位为 \(0,1\) 的分开考虑,建出将两边各自的 \(\text{Kruskal}\) 重构树,然后用 \(\text{01trie}\) 查询两边 \(\operatorname{xor}\) 得到的最小值,得到当前这层的 \(\text{Kruskal}\) 重构树。

正确性是因为每次让大的位出现得尽量小,使位数更低的妥协,而 \(2\times 2^{d-1}\leq 2^d\),得到的一定是最优方案之一。

每个点会被查询至多 \(\log V\) 次,每次 \(\text{01trie}\) 上的点查询需要 \(O(\log V)\),这部分总时间为 \(O(n\log^2V)\)

注意这样将相同的数分到一份时异或为 \(0\),也要建出其重构树。

这之后每次查询就能在 \(\text{Kruskal}\) 重构树上,倍增得到能到达的点集所在的 \(dfn\) 序范围。

\(A,B,C\) 三图各自 \(dfn\) 序范围是 \([la,ra],[lb,rb],[lc,rc]\)

那查询转为询问满足 \(\begin{cases}la\leq dfna_i\leq ra\\lb\leq dfnb_i\leq rb\\lc\leq dfnc_i\leq rc\end{cases}\)\(i\) 的个数。

这就是个明显的三维偏序,可以通过三维差分拆成八个询问后,一个 \(\text{cdq}\) 分治轻松解决。

这部分时间复杂度为 \(O(m\log m\log n)\)

总时间复杂度为 \(O(n\log^2V+m\log n\log m)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,K=28;
const int inf=2e8;
int n,m,x,y,nn,tt,v,tot;char ch;
int a[N][3],son[N][2][3],w[N][3];
struct trie{int son[2],sz;}_[N*K];
int _rt,_tot,_v;bool _b;
inline void read(int &x){
	x=0;ch=getchar();while(ch<48)ch=getchar();
	while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(int x){if(x>9)write(x/10);putchar(48+x%10);}
void update(int &k,int x,int i){
	if(!k)k=++_tot;++_[k].sz;
	if(~i)update(_[k].son[(x>>i)&1],x,i-1);
}
void inquiry(int k,int x,int i){if(~i){
	_b=(x>>i)&1;
	if(_[_[k].son[_b]].sz)inquiry(_[k].son[_b],x,i-1);
	else _v+=1<<i,inquiry(_[k].son[!_b],x,i-1);
}}
inline void _init(){
	static int i;
	for(i=1;i<=_tot;++i)_[i].son[0]=_[i].son[1]=_[i].sz=0;
	_rt=_tot=0;
}
struct node{
	int v,id;node()=default;
	node(int _v,int _id):v(_v),id(_id){}
}p[N],pl[N],pr[N];
inline int build_(int l,int r,int j){
	if(l^r){
		int mid=(l+r)>>1,tmp=++nn;
		son[tmp][0][j]=build_(l,mid,j);
		son[tmp][1][j]=build_(mid+1,r,j);
		return tmp;
	}
	return p[l].id;
}//权值相同时建树 
int build(int l,int r,int d,int j){
	int i;
	if(l^r&&~d){
		int lcnt=0,rcnt=0,tmp;bool b;
		for(i=l;i<=r;++i){
			b=(p[i].v>>d)&1;
			if(b)pr[++rcnt]=p[i];
			else pl[++lcnt]=p[i];
		}
		for(i=1;i<=lcnt;++i)p[l+i-1]=pl[i];
		for(i=1;i<=rcnt;++i)p[r-i+1]=pr[i];
		if(lcnt&&rcnt){
			tmp=++nn;w[tmp][j]=inf;
			son[tmp][0][j]=build(l,l+lcnt-1,d-1,j);
			for(i=r-rcnt+1;i<=r;++i){
				_v=0,inquiry(_rt,p[i].v,K);
				w[tmp][j]=min(w[tmp][j],_v);
			}
			son[tmp][1][j]=build(r-rcnt+1,r,d-1,j);
			return tmp;
		}
		else return build(l,r,d-1,j);
	}
	else {update(_rt,p[l].v,K);return build_(l,r,j);}
}//建kruskal重构树 
int dfn[N][3],dfnr[N][3],rev[N],sz[N][3],f[N][19][3];
void dfs(int x,int anc,int j){
	f[x][0][j]=anc;int i,y;
	for(i=1;i^19;++i)f[x][i][j]=f[f[x][i-1][j]][i-1][j];
	if(y=son[x][0][j])dfs(y,x,j),sz[x][j]+=sz[y][j];
	if(y=son[x][0][j])dfn[x][j]=dfn[y][j];
	if(y=son[x][1][j])dfs(y,x,j),sz[x][j]+=sz[y][j];
	else dfn[x][j]=++tt,sz[x][j]=1;
}
void init(){
	register int i,j,rt;
	for(j=0;j<3;++j){
		for(i=1;i<=n;++i)p[i]=node(a[i][j],i);
		nn=n;rt=build(1,n,K,j);tt=0;dfs(rt,0,j);w[0][j]=inf;
		for(i=1;i<=nn;++i)dfnr[i][j]=dfn[i][j]+sz[i][j]-1;
		_init();
	}
	for(i=1;i<=n;++i)if(sz[i][0]==1)rev[dfn[i][0]]=i;
}
inline int find(int x,int v,int j){
	static int i;
	for(i=18;~i;--i)if(w[f[x][i][j]][j]<=v)x=f[x][i][j];
	return x;
}//倍增 
struct cdq{
	int a,b,c,id;char opt;cdq()=default;//opt=0 : mdf || opt=1,-1 : inq
	cdq(int _a,int _b,int _c,int _id,char _opt):
		a(_a),b(_b),c(_c),id(_id),opt(_opt){}
}q[N<<2],q0[N<<2];
inline bool cmpa(cdq x,cdq y){return x.a^y.a?x.a<y.a:!x.opt;}
inline bool cmpb(cdq x,cdq y){return x.b^y.b?x.b<y.b:!x.opt;}
int t_[N],ti,res,t[3],ans[N];
#define lowbit(i) i&(-i)
inline void update(int pos,int v){for(ti=pos;ti<=n;ti+=lowbit(ti))t_[ti]+=v;}
inline void inquiry(int pos){res=0;for(ti=pos;ti;ti-=lowbit(ti))res+=t_[ti];}
void solve(int l,int r){if(l^r){
	int mid=(l+r)>>1,i;
	solve(l,mid);solve(mid+1,r);
	merge(q+l,q+mid+1,q+mid+1,q+r+1,q0+l,cmpb);
	for(i=l;i<=r;++i)q[i]=q0[i];
	for(i=l;i<=r;++i){
		if(q[i].opt&&q[i].a>mid)inquiry(q[i].c),ans[q[i].id]+=res*q[i].opt;
		else if(!q[i].opt&&q[i].a<=mid)update(q[i].c,1);
	}
	for(i=l;i<=r;++i)if(!q[i].opt&&q[i].a<=mid)update(q[i].c,-1);
}}//cdq
inline void cdq_add(int i){
	static int la,ra,lb,rb,lc,rc;
	la=dfn[t[0]][0]-1,ra=dfnr[t[0]][0];
	lb=dfn[t[1]][1]-1,rb=dfnr[t[1]][1];
	lc=dfn[t[2]][2]-1,rc=dfnr[t[2]][2];
	q[++tot]=cdq(ra,rb,rc,i,1);q[++tot]=cdq(la,lb,lc,i,-1);
	q[++tot]=cdq(la,rb,rc,i,-1);q[++tot]=cdq(ra,lb,lc,i,1);
	q[++tot]=cdq(ra,lb,rc,i,-1);q[++tot]=cdq(la,rb,lc,i,1);
	q[++tot]=cdq(ra,rb,lc,i,-1);q[++tot]=cdq(la,lb,rc,i,1);
}//三维差分 
main(){
	read(n);read(m);register int i,j;
	for(j=0;j<3;++j)for(i=1;i<=n;++i)read(a[i][j]);
	init();
	for(i=1;i<=m;++i){
		for(j=0;j<3;++j)read(v),read(x),t[j]=find(x,v,j);
		cdq_add(i);
	}
	for(i=1;x=rev[i],i<=n;++i)q[++tot]=cdq(i,dfn[x][1],dfn[x][2],0,0);
	sort(q+1,q+tot+1,cmpa);
	for(i=1;i<=tot;++i)q[i].a=i;
	solve(1,tot);
	for(i=1;i<=m;++i)write(ans[i]),putchar('\n');
}
posted @ 2021-11-11 22:41  Y_B_X  阅读(51)  评论(0编辑  收藏  举报