HihoCoder - 1236 Scores (五维偏序,分块+bitset)
题意:给定n个五维空间上的点,以及m组询问,每组询问给出一个点,求五个维度都不大于它的点有多少个,强制在线。
神仙题
单独考虑每个维度,把所有点按这个维度上的大小排序,然后分成T块,每块用一个bitset记录这个块以及之前的块中包含的点的集合的前缀和,并用mx[i][j]来记录第i维上大小为j的点所对应的最右边的块。对于每个询问的点,也是单独考虑每个维度,找到每个维度上对应的块,把这个块之前的块的前缀和加上,然后再暴力加上这个块中所有当前维度不超过它的点,五个维度取个交集就行了。
预处理复杂度$O(n+\frac{nT}{B})$,单次询问复杂度$O(\frac{n}{T}+\frac{n}{B})$,总复杂度$O(n+\frac{nT}{B}+\frac{qn}{T}+\frac{qn}{B})$,理论上T=B(约为64)时复杂度最低。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=5e4+100,M=64,inf=0x3f3f3f3f; 5 struct D { 6 int x,i; 7 bool operator<(const D& b)const {return x<b.x;} 8 } a[5][N]; 9 int n,m,k,sqrtn,in[N],mx[5][N],L[N/M],R[N/M],nb; 10 bitset<N> bs[5][N/M],ans,now; 11 int solve(int* b) { 12 ans.set(); 13 for(int i=0; i<5; ++i) { 14 now.reset(); 15 int j=mx[i][b[i]]; 16 if(j>0)now|=bs[i][j-1]; 17 for(int k=L[j]; k<n&&a[i][k].x<=b[i]; ++k)now.set(a[i][k].i); 18 ans&=now; 19 } 20 return ans.count(); 21 } 22 int main() { 23 int T; 24 for(scanf("%d",&T); T--;) { 25 scanf("%d%d",&n,&m); 26 memset(L,-1,sizeof L); 27 memset(mx,0,sizeof mx); 28 for(int j=0; j<n; ++j) {nb=(in[j]=j/M)+1; if(!~L[in[j]])L[in[j]]=j; R[in[j]]=j;} 29 for(int i=0; i<5; ++i)for(int j=0; j<nb; ++j)bs[i][j].reset(); 30 for(int i=0; i<n; ++i) 31 for(int j=0; j<5; ++j)scanf("%d",&a[j][i].x),a[j][i].i=i; 32 for(int i=0; i<5; ++i)sort(a[i],a[i]+n); 33 for(int i=0; i<5; ++i) { 34 for(int j=0; j<n; ++j) { 35 mx[i][a[i][j].x]=in[j]; 36 bs[i][in[j]].set(a[i][j].i); 37 } 38 for(int j=2; j<=m; ++j)mx[i][j]=max(mx[i][j],mx[i][j-1]); 39 for(int j=1; j<nb; ++j)bs[i][j]|=bs[i][j-1]; 40 } 41 scanf("%d",&k); 42 for(int ans=0; k--;) { 43 int b[5]; 44 for(int i=0; i<5; ++i)scanf("%d",&b[i]),b[i]^=ans; 45 printf("%d\n",ans=solve(b)); 46 } 47 } 48 return 0; 49 }