1926: [Sdoi2010]粟粟的书架
https://www.lydsy.com/JudgeOnline/problem.php?id=1926
分析:
二分 前缀和 主席树。
分成两问做,第一问预处理一个前缀和val[i][j][k],矩阵左上角(1,1),右下角(i,j),大于等于k的和,同样记录有多少个数num。二分最小的数。
第二问,建立主席树,同样二分一个最小值k,然后求出大于等于k的和。判断。
注意判断以下边界,大于等于k的可能全选上。
代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 7 inline int read() { 8 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 9 for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 10 } 11 12 int n,m,T; 13 14 namespace work1 { 15 const int N = 205; 16 int val[N][N][1002], num[N][N][1002], p[N][N], Mx; 17 18 int getval(int a1,int b1,int a2,int b2,int k) { 19 return val[a2][b2][k] - val[a1-1][b2][k] - val[a2][b1-1][k] + val[a1-1][b1-1][k]; 20 } 21 int getnum(int a1,int b1,int a2,int b2,int k) { 22 return num[a2][b2][k] - num[a1-1][b2][k] - num[a2][b1-1][k] + num[a1-1][b1-1][k]; 23 } 24 void solve() { 25 for (int i=1; i<=n; ++i) 26 for (int j=1; j<=m; ++j) 27 p[i][j] = read(), Mx = max(Mx, p[i][j]); 28 for (int k=0; k<=Mx; ++k) 29 for (int i=1; i<=n; ++i) 30 for (int j=1; j<=m; ++j) { 31 val[i][j][k] += val[i-1][j][k] + val[i][j-1][k] - val[i-1][j-1][k] + (p[i][j]>=k ? p[i][j] : 0); 32 num[i][j][k] += num[i-1][j][k] + num[i][j-1][k] - num[i-1][j-1][k] + (p[i][j]>=k ? 1 : 0); 33 } 34 while (T--) { 35 int a1 = read(), b1 = read(), a2 = read(), b2 = read(), h = read(); 36 if (getval(a1, b1, a2, b2, 0) < h) { 37 puts("Poor QLW"); continue ; 38 } 39 int L = 0, R = Mx, ans = -1; 40 while (L <= R) { 41 int mid = (L + R) >> 1; 42 if (getval(a1, b1, a2, b2, mid) >= h) ans = mid, L = mid + 1; 43 else R = mid - 1; 44 } 45 if (ans == -1) { 46 puts("Poor QLW"); continue ; 47 } 48 printf("%d ",getnum(a1, b1, a2, b2, ans) - (getval(a1, b1, a2, b2, ans) - h) / ans); 49 // 大于等于ans的并不一定取完。一共需要大于等于h,所以减去使得大于h后的最多的数,就是ans 50 } 51 } 52 } 53 namespace work2{ 54 const int N = 500005; 55 int ls[N*20], rs[N*20], sum[N*20], siz[N*20], Root[N], CntNode; 56 57 void update(int l,int r,int &rt,int last,int p) { // 权值线段树,p既是坐标也是权值 58 rt = ++CntNode; 59 sum[rt] = sum[last] + p; 60 siz[rt] = siz[last] + 1; 61 int mid = (l + r) >> 1; 62 if (l == r) return ; 63 if (p <= mid) { 64 rs[rt] = rs[last]; 65 update(l, mid, ls[rt], ls[last], p); 66 } 67 else { 68 ls[rt] = ls[last]; 69 update(mid+1, r, rs[rt], rs[last], p); 70 } 71 } 72 int query(int l,int r,int Head,int Tail,int x) { 73 if (l == r) { 74 return ceil((double)x/l); // 向上取整,保证大于等于x 75 return x / l; 76 } 77 int mid = (l + r) >> 1, t = sum[rs[Tail]] - sum[rs[Head]]; 78 if (x > t) return siz[rs[Tail]] - siz[rs[Head]] + query(l, mid, ls[Head], ls[Tail], x-t); 79 return query(mid+1, r, rs[Head], rs[Tail], x); 80 } 81 82 void solve() { 83 for (int i=1; i<=m; ++i) { 84 int x = read(); 85 update(1, 1000, Root[i], Root[i-1], x); 86 } 87 while (T--) { 88 int a1 = read(), b1 = read(), a2 = read(), b2 = read(), h = read(); 89 if (sum[Root[b2]] - sum[Root[b1-1]] < h) { 90 puts("Poor QLW"); continue ; 91 } 92 printf("%d ",query(1, 1000, Root[b1-1], Root[b2], h)); 93 } 94 } 95 } 96 int main() { 97 n = read(), m = read(), T = read(); 98 if (n != 1) work1::solve(); 99 else work2::solve(); 100 return 0; 101 }