1. 程式人生 > 其它 >P6466-分散層疊演算法(Fractional Cascading)【模板】

P6466-分散層疊演算法(Fractional Cascading)【模板】

正題

題目連結:https://www.luogu.com.cn/problem/P6466


題目大意

給出\(k\)個長度為\(n\)的有序序列,\(q\)次詢問給出\(x\),求所有序列中\(x\)的後繼的異或和。

強制線上

\(1\leq k\leq 100,1\leq n\leq 10^4,1\leq q\leq 5\times 10^5\)


解題思路

一個很神奇的演算法,考慮如果我們將一個序列的偶數位提取出來,我們得到在這些偶數位中\(x\)的後繼的話,那麼原來序列中的後繼中的距離不會超過\(1\)

所以我們可以得到一個演算法,考慮原來的序列為\(a_i\),開始取出\(b_k=a_k\),然後對於\(i\)

\(k-1\)\(1\)\(b_{i+1}\)的偶數位提取出來和\(a_i\)合併成一個新的有序\(b_i\)

那麼求解時我們就可以只需要查詢\(b_{1}\)中的後繼了。

時間複雜度:\(O(nk+q(k+\log n))\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int K=110,N=2e4+10;
int n,k,q,d,b[K][N],c[K][N];
int nxt[K][N],p[K][N],l[K];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c))x=(x<<1)+(x<<3)+c-48,c=getchar();
	return x*f;
}
void print(int x)
{if(x>9)print(x/10);putchar(x%10+48);return;}
int main()
{
	n=read();k=read();q=read();d=read();
	for(int i=1;i<=k;i++){
		for(int j=1;j<=n;j++)
			b[i][j]=read();
	}
	for(int i=1;i<=n;i++)c[k][i]=b[k][i];
	l[k]=n;
	for(int i=k-1;i>=1;i--){
		int z=2;
		for(int j=1;j<=n;j++){
			while(z<=l[i+1]&&c[i+1][z]<=b[i][j]){
				c[i][++l[i]]=c[i+1][z];
				p[i][l[i]]=z;
				nxt[i][l[i]]=b[i][j];
				z+=2;
			}
			c[i][++l[i]]=b[i][j];
		}
		while(z<=l[i+1]){
			c[i][++l[i]]=c[i+1][z];
			p[i][l[i]]=z;z+=2;
		}
		for(int j=l[i],last=l[i+1];j>=1;j--)
			if(p[i][j])last=p[i][j];
			else nxt[i][j]=-last;
	}
	int las=0;
	for(int z=1;z<=q;z++){
		int x,ans=0,i=1;x=read();x^=las;
		int pos=lower_bound(c[1]+1,c[1]+1+l[1],x)-c[1];
		ans^=p[i][pos]?nxt[i][pos]:c[i][pos];
		while(i<=k){
			pos=p[i][pos]?p[i][pos]:-nxt[i][pos];
			pos--;i++;
			while(pos<=l[i]&&c[i][pos]<x)pos++;
			ans^=p[i][pos]?nxt[i][pos]:c[i][pos];
		}
		las=ans;
		if(z%d==0)print(ans),putchar('\n');
	}
	return 0;
}