SDOI2010粟粟的書架
阿新 • • 發佈:2018-12-06
題目傳送:https://www.luogu.org/problemnew/show/P2468
這是一個二合一的題目,前50% \(n!=1\)的分數中,我們考慮用動態規劃來做。
設\(sum[i][j][k]\)表示大於等於k的\([1,1]\)到\([i,j]\)範圍內的數的總和,然後用\(num[i][j][k]\)表示數的個數。
之後就可以用二維字首和來維護了,具體見程式碼subtask1
後50%的題目因為資料範圍很大,所以我們只能考慮使用log複雜度的資料結構,因為還涉及到區間排名的問題,所以自然查詢的時候要換成主席樹。
不需要離散化。在查詢的時候因為是查詢大於等於當前查詢值的數的個數,所以我們要從右子樹開始查詢(顯然是要從大到小),之後如果右子樹的總和大於等於當前查詢值,那麼就進入右子樹,如果不是,那麼加上右子樹的數的個數然後查詢左子樹。之後要注意的是如果到了葉子節點,我們要求的個數等於還需要的總和除以該節點值的向上取整。
具體見程式碼subtask2
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define MAXN 1000*10+500000*15+10 int n,m,kkk; namespace subtask1 { int maxx=0; int sum[210][210][1010],num[210][210][1010],a[1010][1010]; inline int max(int x,int y){return x>y?x:y;} inline int getsum(int x1,int x2,int y1,int y2,int k){return sum[x2][y2][k]-sum[x1-1][y2][k]-sum[x2][y1-1][k]+sum[x1-1][y1-1][k];} inline int getnum(int x1,int x2,int y1,int y2,int k){return num[x2][y2][k]-num[x1-1][y2][k]-num[x2][y1-1][k]+num[x1-1][y1-1][k];} inline void init() { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]),maxx=max(maxx,a[i][j]); for(int k=0;k<=maxx;k++) for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { sum[i][j][k]=sum[i][j-1][k]+sum[i-1][j][k]-sum[i-1][j-1][k]+(a[i][j]>=k?a[i][j]:0); num[i][j][k]=num[i][j-1][k]+num[i-1][j][k]-num[i-1][j-1][k]+(a[i][j]>=k?1:0); } } inline void work(int x1,int x2,int y1,int y2,int h) { if(getsum(x1,x2,y1,y2,0)<h) { printf("Poor QLW\n"); return; } int l=0,r=maxx+1,ans=-0x3f; while(l<r) { int mid=(l+r)>>1; if(getsum(x1,x2,y1,y2,mid)>=h) ans=mid,l=mid+1; else r=mid; } if(ans==-0x3f) { printf("Poor QLW\n"); return; } printf("%d\n",getnum(x1,x2,y1,y2,ans)-(getsum(x1,x2,y1,y2,ans)-h)/ans); } } namespace subtask2 { int cnt; int sum[MAXN],siz[MAXN],ls[MAXN],rs[MAXN],rt[MAXN],a[500010]; inline int max(int x,int y){return x>y?x:y;} inline void build(int &x,int l,int r) { x=++cnt; if(l==r) return; int mid=(l+r)>>1; build(ls[x],l,mid); build(rs[x],mid+1,r); } inline int modify(int x,int l,int r,int p) { int to=++cnt; ls[to]=ls[x],rs[to]=rs[x],siz[to]=siz[x]+1,sum[to]=sum[x]+p; if(l==r) return to; int mid=(l+r)>>1; if(mid<p) rs[to]=modify(rs[to],mid+1,r,p); else ls[to]=modify(ls[to],l,mid,p); return to; } inline int query(int u,int v,int l,int r,int k) { if(l==r) return (k+l-1)/l; int mid=(l+r)>>1; int x=sum[rs[v]]-sum[rs[u]]; if(k<=x) return query(rs[u],rs[v],mid+1,r,k); else return siz[rs[v]]-siz[rs[u]]+query(ls[u],ls[v],l,mid,k-x); } inline void work() { int maxx=0; for(int i=1;i<=m;i++) scanf("%d",&a[i]),maxx=max(maxx,a[i]); build(rt[0],1,m); for(int i=1;i<=m;i++) rt[i]=modify(rt[i-1],1,maxx,a[i]); for(int i=1;i<=kkk;i++) { int cur1,cur2,u,v,h; scanf("%d",&cur1),scanf("%d",&u); scanf("%d",&cur2),scanf("%d",&v); scanf("%d",&h); if(sum[rt[v]]-sum[rt[u-1]]<h) { printf("Poor QLW\n"); continue; } printf("%d\n",query(rt[u-1],rt[v],1,maxx,h)); } } } using namespace std; using namespace subtask1; using namespace subtask2; int main() { scanf("%d%d%d",&n,&m,&kkk); if(n!=1) { subtask1::init(); for(int i=1;i<=kkk;i++) { int xx1,xx2,yy1,yy2; int val; scanf("%d%d%d%d%d",&xx1,&yy1,&xx2,&yy2,&val); subtask1::work(xx1,xx2,yy1,yy2,val); } } else subtask2::work(); return 0; }