洛谷P2468 粟粟的书架
题目链接:https://www.luogu.org/problemnew/show/P2468
知识点: 可持久化线段树、二分、前缀和
解题思路:
对于 \(R, C \le 200, M \le 200,000\) 的数据,先处理出前缀和,然后二分取出的数中最小的数。细节请参考 \(solve2()\) 函数。
对于 \(R=1,C \le 500,000,M \le 20,000\) 的数据,维护一棵记录 \([1,1000]\) 的数在各个历史版本的个数和相应的总和的可持久化线段树,将一行上各列的数依序更新进去。细节请参考 \(solve2()\) 及相关函数。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 typedef long long LL; 5 const int inf=0x3f3f3f3f; 6 const int maxc=500003; 7 8 int sum[maxc*15];//记录总和 9 int has[maxc*15];//记录出现次数 10 int lson[maxc*15],rson[maxc*15],T[maxc*15]; 11 int tot; 12 int p1[maxc]; 13 void build(int l,int r,int &rt){ 14 rt=++tot; 15 sum[rt]=has[rt]=0; 16 if(l==r) return; 17 int m=(l+r)>>1; 18 build(l,m,lson[rt]); 19 build(m+1,r,rson[rt]); 20 } 21 void update(int last,int p,int l,int r,int &rt){ 22 rt=++tot; 23 lson[rt] = lson[last]; 24 rson[rt] = rson[last]; 25 has[rt] = has[last] + 1; 26 sum[rt] = sum[last] + p; 27 if (l == r) return; 28 int m = (l + r) >> 1; 29 if (p <= m) update(lson[last], p, l, m, lson[rt]); 30 else update(rson[last], p, m + 1, r, rson[rt]); 31 } 32 //用一种类似二分的方式来查询 33 int query(int s,int t,int h,int l,int r){ 34 int ret=0; 35 while(l<r){ 36 int m=(l+r)>>1; 37 int rch=sum[rson[t]]-sum[rson[s]];//优先用右子树(因为其上的数字较大) 38 if(rch<h)//如果只用右子树仍然不够,则直接加上右子树的总和,再查询左子树 39 ret+=has[rson[t]]-has[rson[s]],h-=rch,s=lson[s],t=lson[t],r=m; 40 else//否则查询右子树 41 s=rson[s],t=rson[t],l=m+1; 42 } 43 ret+=(h-1)/l+1;//答案修正 44 return ret; 45 } 46 void solve1(int R,int C,int M){ 47 build(1,1000,T[0]); 48 for(int i=1;i<=C;i++){ 49 scanf("%d",&p1[i]); 50 update(T[i-1],p1[i],1,1000,T[i]); 51 } 52 int x1,y1,x2,y2,h; 53 for(int i=0;i<M;i++){ 54 scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h); 55 if(sum[T[y2]]-sum[T[y1-1]]<h) printf("Poor QLW\n"); 56 else 57 printf("%d\n",query(T[y1-1],T[y2],h,1,1000)); 58 } 59 } 60 61 int p[203][203]; 62 int have[203][203][1003];//have[x][y][k]: 代表p矩阵中在点(x,y)左上角的子矩阵里大于等于k的数的个数 63 int height[203][203][1003];//height[x][y][k]: 代表p矩阵中在点(x,y)左上角的子矩阵里大于等于k的数的总和 64 int get_have(int x1,int y1,int x2,int y2,int ind){ 65 return have[x2][y2][ind]-have[x1][y2][ind]-have[x2][y1][ind]+have[x1][y1][ind]; 66 } 67 int get_height(int x1,int y1,int x2,int y2,int ind){ 68 return height[x2][y2][ind]-height[x1][y2][ind]-height[x2][y1][ind]+height[x1][y1][ind]; 69 } 70 void solve2(int R,int C,int M){ 71 for(int i=1;i<=R;i++){ 72 for(int j=1;j<=C;j++) 73 scanf("%d",&p[i][j]); 74 } 75 for(int i=1000;i>=0;i--){ 76 for(int x=1;x<=R;x++){ 77 for(int y=1;y<=C;y++){ 78 //转移 79 height[x][y][i]=height[x-1][y][i]+height[x][y-1][i]-height[x-1][y-1][i]+(p[x][y]>=i?p[x][y]:0); 80 have[x][y][i]=have[x-1][y][i]+have[x][y-1][i]-have[x-1][y-1][i]+(p[x][y]>=i?1:0); 81 } 82 } 83 } 84 int x1,y1,x2,y2; 85 int h; 86 while(M--){ 87 scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h); 88 int l=0,r=1001;//二分取出来的数中最小的数,此处 r 如果没有初始化为 1001 的话会 WA 一个点 89 int ans=-1; 90 for(int i=0;i<100;i++){ 91 int m=(l+r)>>1; 92 int tmp=get_height(x1-1,y1-1,x2,y2,m); 93 if(tmp>=h) 94 l=m,ans=get_have(x1-1,y1-1,x2,y2,m)-(tmp-h)/m;//减掉多余的部分 95 else 96 r=m; 97 } 98 if(ans==-1) printf("Poor QLW\n"); 99 else printf("%d\n",ans); 100 } 101 } 102 103 int main(){ 104 // freopen("in.txt","r",stdin); 105 int R,C,M; 106 scanf("%d%d%d",&R,&C,&M); 107 if(R==1) solve1(R,C,M); 108 else solve2(R,C,M); 109 return 0; 110 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”