北京网络赛的题- -。当时没思路,听大神们说是分块+bitset,想了一下发现确实可做,就试了一下,T了好多次终于过了
题意:
初始有n个人,每个人有五种能力值,现在有q个查询,每次查询给五个数代表查询的五种能力值,输出有多少个人每种能力值都比查询的小
n和q都是50000,每种能力值最大也为50000
思路:
对于某一个大小的能力值,有哪些人的此项能力值比他小可以用一个50000的bitset表示。这样我们在查询的时候就可以拿到5个对应的bitset,对其进行and就可以得出最终的人数
这样每组询问的复杂度为5*n/32 总复杂度较高但勉强可以接受。
但是这样做有一点问题就是要开5*50000个大小为50000的bitset,显然这样会超内存。。于是想到将bitset分块,这样虽然询问时需要花费一定时间(sqrt)来找到5个bitset
但是显然这个时间是小于n/32的,所以并不会对总时间造成较大的影响,于是显然可行
注意一点就是分块的时候不仅要按照分数段分块,还要按照人数分块,否则如果某个分数段人数太多就gg了
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 bitset<50010>b[7][500]; 4 bitset<50010>tmp; 5 int n,m,q; 6 int a[50010][7]; 7 vector<int>v[7][50010]; 8 bitset<50010>p[7]; 9 int tt=10; 10 int now[5]; 11 vector<int>d[7]; 12 int main() 13 { 14 //freopen("in.txt","r",stdin); 15 int T; 16 scanf("%d",&T); 17 while(T--) 18 { 19 scanf("%d%d",&n,&m); 20 for(int i=0; i<5; i++) 21 { 22 d[i].clear(); 23 for(int j=1; j<=m; j++) 24 { 25 v[i][j].clear(); 26 } 27 } 28 for(int i=0;i<5;i++) 29 { 30 for(int j=1;j<=300;j++) 31 { 32 b[i][j].reset(); 33 } 34 } 35 for(int i=0; i<n; i++) 36 { 37 for(int j=0; j<5; j++) 38 { 39 scanf("%d",a[i]+j); 40 v[j][a[i][j]].push_back(i); 41 } 42 } 43 int tm=0; 44 for(int i=0; i<5; i++) 45 { 46 tm=0; 47 tmp.reset(); 48 b[i][0]=tmp; 49 d[i].push_back(0); 50 for(int j=0; j<=m; j++) 51 { 52 for(int to:v[i][j]) 53 { 54 tmp[to]=1; 55 tm++; 56 } 57 if(tm>sqrt(n)||j-d[i][(int)d[i].size()-1]>sqrt(m)||j==m) 58 { 59 b[i][(int)d[i].size()]=tmp; 60 d[i].push_back(j); 61 tm=0; 62 } 63 } 64 } 65 scanf("%d",&q); 66 int pre=0; 67 tm=0; 68 while(q--) 69 { 70 for(int i=0; i<5; i++) 71 { 72 scanf("%d",now+i); 73 now[i]^=pre; 74 } 75 for(int i=0; i<5; i++) 76 { 77 int kk=upper_bound(d[i].begin(),d[i].end(),now[i])-d[i].begin(); 78 kk--; 79 p[i]=b[i][kk]; 80 for(int j=d[i][kk]+1; j<=now[i]; j++) 81 { 82 for(int to:v[i][j]) 83 { 84 p[i][to]=1; 85 } 86 } 87 //p&=tmp; 88 } 89 for(int i=1;i<5;i++) 90 { 91 p[0]&=p[i]; 92 } 93 int ans=p[0].count(); 94 pre=ans; 95 printf("%d ",ans); 96 } 97 } 98 return 0; 99 }