P2468 [SDOI2010]粟粟的书架 动态规划,主席树,二分答案
題目
洛谷題目鏈接
題解
題目的數據范圍非常奇怪,一半是200?200200?200的矩陣,另一半是1?500001?50000的矩陣,顯然是一道二合一的題目,但是不一樣的地方也就在與數據結構的選擇不一樣,而我們解這道題使用的算法是一樣的,即都是二分法。
這道題所需要的數據結構應該能提供如下的功能:在O(1)O(1)或O(logn)O(logn)的時間復雜度內查詢矩陣某區間內厚度大于xx的書本數,以及厚度大于xx的書的頁數總和。
根據上述數據結構的功能,我們可以設計一個二分算法。
即我們二分我們選取的書的最小厚度midmid。checkcheck時候就checkcheck一下矩陣內厚度≥mid≥mid的所有書的厚度之和是否能達到要求。
這樣的話,二分完之后,所有厚度大于ll的書的個數就是我們的答案!
錯!因為有可能厚度等于ll的書被多余使用了,因此,我們還要去掉一部分厚度為ll的書,使得總厚度仍然滿足要求,但是答案變小。
算法我們已經涉及完了。
現在我們想一下怎么樣設計數據結構:
當矩陣大小為200?200200?200的時候,我們定義val[i][j][k]val[i][j][k]表示矩陣區間[1,i][1,j][1,i][1,j]部分,厚度≥k≥k的書的總厚度,類似的定意num[i][j][k]num[i][j][k]表示個數。
這樣只需要dpdp一下就ok了。
當矩陣大小為1?5000001?500000的時候,涉及到區間操作,我們可以使用兩棵主席樹,一顆維護和,另一顆維護個數。
代碼
// luogu-judger-enable-o2 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define pr(x) cout<<#x<<":"<<x<<endl using namespace std; const int maxn = 500007; struct segtree{int root[maxn*20];int val[maxn*20];int lson[maxn*20];int rson[maxn*20];int id = 0;void init(){memset(root,0,sizeof(root));memset(val,0,sizeof(val));memset(lson,0,sizeof(lson));memset(rson,0,sizeof(rson));id = 0;}int ins(int &rt,int l,int r,int pos,int v){int nrt = ++id;lson[nrt] = lson[rt];rson[nrt] = rson[rt];val[nrt] = val[rt] + v;rt = nrt;if(l == r) return 0;int mid = (l+r)/2;if(pos <= mid) ins(lson[rt],l,mid,pos,v);else ins(rson[rt],mid+1,r,pos,v);}int query(int rt,int l,int r,int ul,int ur){if(!rt || ul > r || ur < l) return 0;if(ul <= l && r <= ur) return val[rt];int mid = (l+r)/2;int r1 = query(lson[rt],l,mid,ul,ur);int r2 = query(rson[rt],mid+1,r,ul,ur);return r1+r2;} }*seg,*segcnt; int R,C,M; int ck1(int mid,int y1,int y2){int sm = seg->query(seg->root[y2],1,1000,mid,1000);sm -= seg->query(seg->root[y1-1],1,1000,mid,1000);return sm; } void solve1(){seg = new segtree;segcnt = new segtree;seg->init();segcnt->init();for(int i = 1;i <= C;++i){int v;scanf("%d",&v);seg->root[i] = seg->root[i-1];segcnt->root[i] = segcnt->root[i-1];seg->ins(seg->root[i],1,1000,v,v);segcnt->ins(segcnt->root[i],1,1000,v,1);}for(int i = 1;i <= M;++i){int x1,y1,x2,y2,h;scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);int l = 1,r = 1000;while(l < r){int mid = (l+r+1) / 2;if(ck1(mid,y1,y2) >= h) l = mid;else r = mid-1;}if(ck1(l,y1,y2) < h) puts("Poor QLW");else {int ans = segcnt->query(segcnt->root[y2],1,1000,l,1000);ans -= segcnt->query(segcnt->root[y1-1],1,1000,l,1000);int delta = ck1(l,y1,y2) - h;ans -= delta / l;printf("%d\n",ans);}} } int (*val)[201][1001],(*vc)[201][1001]; int ck2(int va[201][201][1001],int mid,int x1,int y1,int x2,int y2){int ans = va[x2][y2][mid] + va[x1-1][y1-1][mid]- va[x2][y1-1][mid] - va[x1-1][y2][mid];return ans; } void solve2(){val = new int[201][201][1001];vc = new int[201][201][1001];memset(val,0,sizeof(val));memset(vc,0,sizeof(vc));for(int i = 1;i <= R;++i) for(int j = 1;j <= C;++j){int tmp;scanf("%d",&tmp);for(int k = 1;k <= 1000;++k){val[i][j][k] = val[i][j-1][k] + val[i-1][j][k]- val[i-1][j-1][k];vc[i][j][k] = vc[i][j-1][k] + vc[i-1][j][k] - vc[i-1][j-1][k];if(tmp >= k) val[i][j][k] += tmp,vc[i][j][k] ++; }}for(int i = 1;i <= M;++i){int x1,y1,x2,y2,h;scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);int l = 1,r = 1000;while(l < r){int mid = (l+r+1) / 2;if(ck2(val,mid,x1,y1,x2,y2) >= h) l = mid;else r = mid-1;}if(ck2(val,l,x1,y1,x2,y2) < h) puts("Poor QLW");else {int ans = ck2(vc,l,x1,y1,x2,y2);int delta = (ck2(val,l,x1,y1,x2,y2)-h)/l;ans -= delta;printf("%d\n",ans);}} }int main() {cin>>R>>C>>M;if(R == 1) solve1();else solve2();return 0; }總結
以上是生活随笔為你收集整理的P2468 [SDOI2010]粟粟的书架 动态规划,主席树,二分答案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英特尔酷睿 i9-14900K 开启 A
- 下一篇: 小宇宙歌词 小宇宙完整版歌词