线段树套treap+dp(BZOJ1926)
2015-03-02 14:59
309 查看
1926: [Sdoi2010]粟粟的书架
Time Limit: 20 Sec Memory Limit:552 MB
Submit: 482 Solved: 177
[Submit][Status]
Description
Input
第一行是三个正整数R, C, M。 接下来是一个 R行C 列的矩阵,从上到下、从左向右依次给出了每本书的页数Pi,j。 接下来 M行,第 i 行给出正整数x1i, y1i, x2i, y2i, Hi,表示第i 天的指定区域是﹙x1i, y1i﹚与﹙x2i, y2i﹚间的矩形,总页数之和要求不低于 Hi。 保证 1≤x1i≤x2i≤R,1≤y1i≤y2i≤C。Output
有M行,第i 行回答粟粟在第 i 天时为摘到苹果至少需要拿取多少本书。如果即使取走所有书都无法摘到苹果,则在该行输出“Poor QLW”(不含引号)。Sample Input
5 5 714 15 9 26 53
58 9 7 9 32
38 46 26 43 38
32 7 9 50 28
8 41 9 7 17
1 2 5 3 139
3 1 5 5 399
3 3 4 5 91
4 1 4 1 33
1 3 5 4 185
3 3 4 3 23
3 1 3 3 108
Sample Output
615
2
Poor QLW
9
1
3
HINT
对于 10%的数据,满足 R, C≤10;对于 20%的数据,满足 R, C≤40;
对于 50%的数据,满足 R, C≤200,M≤200,000;
另有 50%的数据,满足 R=1,C≤500,000,M≤20,000;
对于 100%的数据,满足 1≤Pi,j≤1,000,1≤Hi≤2,000,000,000。
思路:分情况讨论,对于R!=1的来说,用dp递推一下就行了dp[i][j][k],表示坐标(0,0)到(i,j)页数为k的有多少本,查询的时候从最高页数开始暴力就行了
对于R==1的情况则要用树套树进行处理:首先二分页数,然后查询大于等于这个页数的总页数是多少
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<vector> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #include<algorithm> using namespace std; const int maxn=500010; int a[maxn]; int R,C,M; int tot; int f[205][205][1005]; struct Node { int ch[2]; int r;//优先级 int v;//值 int s; int sum; int cnt;//自身重复次数 void init(int val){v=val;ch[0]=ch[1]=0;s=cnt=1;r=rand();sum=0;} int cmp(int x)const { if(x==v)return -1; return x<v?0:1; } }tree[maxn*20]; void maintain(int x) { tree[x].s=tree[x].cnt; tree[x].s+=tree[tree[x].ch[0]].s+tree[tree[x].ch[1]].s; tree[x].sum=tree[x].cnt*tree[x].v+tree[tree[x].ch[0]].sum+tree[tree[x].ch[1]].sum; } void rotate(int &o,int d) { int k=tree[o].ch[d^1]; tree[o].ch[d^1]=tree[k].ch[d]; tree[k].ch[d]=o; maintain(o); maintain(k); o=k; } void insert(int &o,int x) { if(!o) { o=++tot; tree[o].init(x); } else { if(x==tree[o].v)tree[o].cnt++; else { int d=(x<tree[o].v?0:1); insert(tree[o].ch[d],x); if(tree[tree[o].ch[d]].r>tree[o].r) rotate(o,d^1); } } maintain(o); } void remove(int &o,int x) { if(!o)return; int d=tree[o].cmp(x); if(d==-1) { int u=o; if(tree[o].cnt>1)tree[o].cnt--; else if(tree[o].ch[0]&&tree[o].ch[1]) { int d2=(tree[tree[o].ch[0]].r>tree[tree[o].ch[1]].r?1:0); rotate(o,d2); remove(tree[o].ch[d2],x); } else { if(!tree[o].ch[0])o=tree[o].ch[1]; else o=tree[o].ch[0]; } } else remove(tree[o].ch[d],x); if(o)maintain(o); } //返回最大值 int get_max(int o) { while(tree[o].ch[0])o=tree[o].ch[0]; return tree[o].v; } //返回最小值 int get_min(int o) { while(tree[o].ch[1])o=tree[o].ch[1]; return tree[o].v; } //返回val的前驱,如果没有的话返回y //y的初值可赋成0,表示没有前驱 int get_pred(int o,int val,int y) { if(!o)return y; if(tree[o].v<=val)//注意大于等于号 return get_pred(tree[o].ch[1],val,tree[o].v); else return get_pred(tree[o].ch[0],val,y); } //返回val的后继,如果没有的话返回y //y的初值可赋成0,表示没有后继 int get_succ(int o,int val,int y) { if(!o)return y; if(tree[o].v>=val)return get_succ(tree[o].ch[0],val,tree[o].v); else return get_succ(tree[o].ch[1],val,y); } //返回第k大的元素的值 int get_kth(int o,int k) { if(!o)return 0; if(k<=tree[tree[o].ch[0]].s)return get_kth(tree[o].ch[0],k); else if(k>tree[tree[o].ch[0]].s+tree[o].cnt) return get_kth(tree[o].ch[1],k-tree[tree[o].ch[0]].s-tree[o].cnt); return tree[o].v; } //返回val的排名 int get_rank(int o,int val) { if(!o)return 0; int lsize=tree[tree[o].ch[0]].s; if(val<tree[o].v) return get_rank(tree[o].ch[0],val); else if(val>tree[o].v) return get_rank(tree[o].ch[1],val)+lsize+tree[o].cnt; return lsize+tree[o].cnt; } pair<int,int> getcnt(int o,int val) { if(!o)return make_pair(0,0); int lsize=tree[tree[o].ch[0]].s; if(val<tree[o].v) { pair<int,int> tmp=getcnt(tree[o].ch[0],val); tmp.first+=tree[o].cnt+tree[tree[o].ch[1]].s; tmp.second+=tree[tree[o].ch[1]].sum+tree[o].v*tree[o].cnt; return tmp; } else if(val>tree[o].v) return getcnt(tree[o].ch[1],val); int cnt=tree[tree[o].ch[1]].s+tree[o].cnt; int sum=tree[tree[o].ch[1]].sum+tree[o].v*tree[o].cnt; return make_pair(cnt,sum); } struct IntervalTree { int root[maxn<<2]; void build(int o,int l,int r) { root[o]=0; for(int i=l;i<=r;i++)insert(root[o],a[i]); if(l==r)return; int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); } pair<int,int> query(int o,int l,int r,int q1,int q2,int val) { if(q1<=l&&r<=q2)return getcnt(root[o],val); int mid=(l+r)>>1; if(q2<=mid)return query(o<<1,l,mid,q1,q2,val); if(q1>mid)return query(o<<1|1,mid+1,r,q1,q2,val); pair<int,int> p,q; p=query(o<<1,l,mid,q1,mid,val); q=query(o<<1|1,mid+1,r,mid+1,q2,val); p.first+=q.first; p.second+=q.second; return p; } }tr; int sum[maxn]; void solve1() { for(int i=1;i<=C;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } int x,x1,y1,x2,y2; tot=0; tr.build(1,1,C); pair<int,int> tmp; while(M--) { scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&x); if(sum[y2]-sum[y1-1]<x) { printf("Poor QLW\n"); continue; } int l=0,r=1001; while(l<r) { int mid=l+(r-l+1)/2; tmp=tr.query(1,1,C,y1,y2,mid); if(tmp.second<x)r=mid-1; else l=mid; } tmp=tr.query(1,1,C,y1,y2,l+1); printf("%d\n",(x-tmp.second-1)/l+1+tmp.first); } } int getsum(int x1,int y1,int x2,int y2,int x) { return f[x2][y2][x]-f[x1-1][y2][x]-f[x2][y1-1][x]+f[x1-1][y1-1][x]; } void solve2() { int x,x1,y1,x2,y2,sum; for(int i=1;i<=R;i++) { for(int j=1;j<=C;j++) { scanf("%d",&x); for(int k=1;k<=1000;k++) f[i][j][k]+=f[i-1][j][k]+f[i][j-1][k]-f[i-1][j-1][k]; f[i][j][x]++; } } while(M--) { scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&sum); int cnt=0,ans=0,flag=0; for(int i=1000;i>0;i--) { int tmp=getsum(x1,y1,x2,y2,i); if(cnt+tmp*i<sum)cnt+=tmp*i,ans+=tmp; else { flag=1; ans+=(sum-cnt-1)/i+1; break; } } if(flag)printf("%d\n",ans); else printf("Poor QLW\n"); } } int main() { scanf("%d%d%d",&R,&C,&M); if(R==1)solve1(); else solve2(); return 0; }
相关文章推荐
- 【bzoj 3946】 无聊的游戏 - 线段树套可持久化Treap
- 【BZOJ5210】最大连通子块和 树剖线段树+动态DP
- bzoj 3790: 神奇项链 (manacher+线段树优化DP)
- BZOJ 3295 CQOI 2011 动态逆序对 线段树套Treap
- bzoj 4712: 洪水 树链剖分+线段树优化dp
- [bzoj3196][Tyvj 1730][二逼平衡树] (线段树套treap)
- 【线段树,Treap】BZOJ2770 YY的Treap
- BZOJ 5157: [Tjoi2014]上升子序列 dp 线段树
- 【bzoj2770】YY的Treap 权值线段树
- [线段树 Treap] BZOJ 2770 YY的Treap
- #bzoj3380#小Q的新玩具(DP + set优化 / 线段树优化)
- BZOJ 3196(Tyvj 1730 二逼平衡树-线段树套Treap)
- 【BZOJ】栅栏-线段树优化DP
- bzoj 1835: [ZJOI2010]base 基站选址 线段树优化dp
- 【BZOJ 2957】楼房重建&&Codechef COT5 Count on a Treap&&【NOIP模拟赛】Weed 线段树的分治维护
- 【BZOJ3196】Tyvj 1730 二逼平衡树 线段树套Treap
- BZOJ 1835: [ZJOI2010]base 基站选址(DP,线段树)
- bzoj1835(线段树优化dp)
- BZOJ 4574: [Zjoi2016]线段树/UOJ #196. 【ZJOI2016】线段树 dp
- 【BZOJ 3242】 (环套树、线段树+树形DP?)