生活随笔
收集整理的這篇文章主要介紹了
P2468 [SDOI2010]粟粟的书架 主席树 + 二分 + 二维前缀和
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
傳送門
文章目錄
題意:
給你一個n?mn*mn?m的矩陣,每次詢問一個矩形,左上角是(x1,y1)(x_1,y_1)(x1?,y1?),右上角是(x2,y2)(x_2,y_2)(x2?,y2?),和一個hhh,問是否能選出矩陣中的若干個數使其和≥h\ge h≥h,能的話求出選的個數最少是多少個。
數據范圍:
題意:
初看這題,沒看范圍,還以為是什么高級的數據結構,比如二維主席樹什么的,聽說二維主席樹隨便就能卡成nnn\sqrt nnn?的,所以一直卡著沒什么想法。最后看到了數據范圍,直呼上當了。
可以將這個題分成兩個題來做。
先看第二種n=1,m≤5e5n=1,m \le 5e5n=1,m≤5e5。
這個顯然就變成一個序列,我們只需要在序列上建立主席樹,讓后查詢即可,變成了主席樹裸題。
看第一種n,m≤200n,m\le200n,m≤200。
有一種貪心的策略,那就是盡可能選p(i,j)p(i,j)p(i,j)大的數加上,這樣選出來的一定個數最少,所以我們二分選的最小的p(i,j)p(i,j)p(i,j),讓后現在我們需要快速求出來矩陣中≥p(i,j)\ge p(i,j)≥p(i,j)的數的和,以及數量,這兩個顯然可以預處理出來,具體細節可以看代碼。
注意求數量的時候,由于可能有多個都是p(i,j)p(i,j)p(i,j),所以要減去多余的。
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std
;
typedef long long LL
;
typedef unsigned long long ULL
;
typedef pair
<int,int> PII
;const int N
=1000010,M
=2000,mod
=1e9+7,INF
=0x3f3f3f3f;
const double eps
=1e-6;int n
,m
,q
;
int a
[201][201],g
[201][201][1001],f
[201][201][1001];
int root
[N
],tot
;
struct Node {int l
,r
;int cnt
;int sum
;
}tr
[N
*40];void insert(int p
,int &q
,int l
,int r
,int pos
) {q
=++tot
; tr
[q
]=tr
[p
];tr
[q
].cnt
++; tr
[q
].sum
+=pos
;if(l
==r
) return;int mid
=(l
+r
)>>1;if(pos
<=mid
) insert(tr
[p
].l
,tr
[q
].l
,l
,mid
,pos
);else insert(tr
[p
].r
,tr
[q
].r
,mid
+1,r
,pos
);
}int query(int p
,int q
,int l
,int r
,int sum
) {if(l
==r
) {int cnt
=tr
[q
].cnt
-tr
[p
].cnt
;int one
=(tr
[q
].sum
-tr
[p
].sum
)/cnt
;return (sum
+one
-1)/one
;}LL all
=tr
[q
].sum
-tr
[p
].sum
;if(all
<sum
) return -1;int mid
=(l
+r
)>>1;LL rs
=tr
[tr
[q
].r
].sum
-tr
[tr
[p
].r
].sum
;if(rs
>=sum
) return query(tr
[p
].r
,tr
[q
].r
,mid
+1,r
,sum
);else return query(tr
[p
].l
,tr
[q
].l
,l
,mid
,sum
-rs
)+tr
[tr
[q
].r
].cnt
-tr
[tr
[p
].r
].cnt
;
}void solve1() {n
=m
;for(int i
=1;i
<=n
;i
++) {int x
; scanf("%d",&x
);insert(root
[i
-1],root
[i
],1,M
,x
);}while(q
--) {int x
,xx
,l
,r
,h
; scanf("%d%d%d%d%d",&x
,&l
,&xx
,&r
,&h
);int ans
=query(root
[l
-1],root
[r
],1,M
,h
);if(ans
==-1) puts("Poor QLW");else printf("%d\n",ans
);}
}bool check(int mid
,int x1
,int y1
,int x2
,int y2
,int h
) {LL sum
=f
[x2
][y2
][mid
]-f
[x1
-1][y2
][mid
]-f
[x2
][y1
-1][mid
]+f
[x1
-1][y1
-1][mid
];if(sum
<h
) return false;int ans
=g
[x2
][y2
][mid
+1]-g
[x1
-1][y2
][mid
+1]-g
[x2
][y1
-1][mid
+1]+g
[x1
-1][y1
-1][mid
+1];h
-=f
[x2
][y2
][mid
+1]-f
[x1
-1][y2
][mid
+1]-f
[x2
][y1
-1][mid
+1]+f
[x1
-1][y1
-1][mid
+1];tot
=ans
+(h
+mid
-1)/mid
;return true;
}void solve2() {for(int i
=1;i
<=n
;i
++) for(int j
=1;j
<=m
;j
++) scanf("%d",&a
[i
][j
]);for(int i
=1;i
<=n
;i
++) {for(int j
=1;j
<=m
;j
++) {LL sum1
,sum2
; sum1
=sum2
=0;for(int k
=1000;k
>=1;k
--) {f
[i
][j
][k
]=f
[i
-1][j
][k
]+f
[i
][j
-1][k
]-f
[i
-1][j
-1][k
];g
[i
][j
][k
]=g
[i
-1][j
][k
]+g
[i
][j
-1][k
]-g
[i
-1][j
-1][k
];if(a
[i
][j
]==k
) f
[i
][j
][k
]+=k
,g
[i
][j
][k
]+=1;}}}for(int i
=1;i
<=n
;i
++) {for(int j
=1;j
<=m
;j
++) {LL sum1
,sum2
; sum1
=sum2
=0;for(int k
=1000;k
>=1;k
--) {f
[i
][j
][k
]+=f
[i
][j
][k
+1];g
[i
][j
][k
]+=g
[i
][j
][k
+1];}}}while(q
--) {int x1
,y1
,x2
,y2
,h
; scanf("%d%d%d%d%d",&x1
,&y1
,&x2
,&y2
,&h
);int l
=1,r
=1000,ans
=-1; tot
=-1;while(l
<=r
) {int mid
=(l
+r
)>>1;if(check(mid
,x1
,y1
,x2
,y2
,h
)) ans
=mid
,l
=mid
+1;else r
=mid
-1;}if(ans
==-1) puts("Poor QLW");else printf("%d\n",tot
);}
}int main()
{
scanf("%d%d%d",&n
,&m
,&q
);if(n
==1) { solve1(); }else { solve2(); }return 0;
}
總結
以上是生活随笔為你收集整理的P2468 [SDOI2010]粟粟的书架 主席树 + 二分 + 二维前缀和的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。