可持久化线段树16(二合一题目+区间和妙用)

本文解析了如何利用二分和主席树技巧解决P2468[SDOI2010]粟粟的书架问题,以及R>1时的二维前缀和优化,同时介绍了FJOI2016神秘数和JSOI2018列队的相似策略。重点讲解了R=1时主席树的使用和区间维护技巧,避免了传统方法的时间复杂度问题。
摘要由CSDN通过智能技术生成

P2468 [SDOI2010]粟粟的书架

先观察这个题的数据范围

在这里插入图片描述
显然这是一个二合一的题目。

R = 1 R=1 R=1的时候这道题可以用主席树来做,否则这个题就是一个二维前缀和+二分

先说 R > 1 R>1 R>1的情况:

维护一个二维前缀和,并且要记录大于等于某一值的二维前缀和(小于的取0),查询的时候二分这个值(值域是 [ 1 , 1000 ] [1,1000] [1,1000]),二分这个值域,求得某一值的前缀和,这个前缀和满足大于 H i H_i Hi,然后就可以记录最少需要的书本数。无解的时候需要特判。

再说 R = 1 R=1 R=1的情况,如果再用前缀和+二分的话,时间复杂度会过大,所以直接采取主席树维护区间和的方式即可,同时还要维护区间内部值出现的次数。查询的时候需要向上取整。无解的时候需要特判。

具体做法就是维护区间和,与这两个题很像

P4587 [FJOI2016]神秘数

P4559 [JSOI2018]列队


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <queue>
#include <deque>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
#include <bitset>
#include <set>
#include <map>
//#include <unordered_map>
#define inf 0x7fffffff
//#define ll long long
//#define int long long
//#define double long double
#define re register int
#define void inline void
#define eps 1e-8
//#define mod 1e9+7
#define ls(p) p<<1
#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define P pair < int , int >
#define mk make_pair
using namespace std;
//const int mod=1000000007;
const int M=5e6;
const int maxn=4;
const int len=4;
const int N=5e5+5;//?????????? 4e8
int sum[205][205][1005],cnt[205][205][1005];
int n,m,q;
int a1,b1,a2,b2;
int p[205][205];
int getsum(int k)
{
	return sum[a2][b2][k]-sum[a1-1][b2][k]-sum[a2][b1-1][k]+sum[a1-1][b1-1][k];
}
int getcnt(int k)
{
	return cnt[a2][b2][k]-cnt[a1-1][b2][k]-cnt[a2][b1-1][k]+cnt[a1-1][b1-1][k];
}
void solve2()
{
	int ma=0;
	for(re i=1;i<=n;i++)  for(re j=1;j<=m;j++)  scanf("%d",&p[i][j]),ma=max(p[i][j],ma);
	for(re k=0;k<=ma;k++)  for(re i=1;i<=n;i++)  for(re j=1;j<=m;j++)
	{
		sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k]+(p[i][j]>=k?p[i][j]:(int)0);
		cnt[i][j][k]=cnt[i-1][j][k]+cnt[i][j-1][k]-cnt[i-1][j-1][k]+(p[i][j]>=k);
	} 
	while(q--)
	{
		int k; 
		scanf("%d%d%d%d%d",&a1,&b1,&a2,&b2,&k);
		if(getsum(0)<k)
		{
			puts("Poor QLW");
			continue;
		}
		int l=0,r=ma+1,ans=-1;
		while(l+1<r)
		{
			int mid=(l+r)>>1;
			if(getsum(mid)>=k)  l=mid,ans=mid;
			else  r=mid;
		} 
		if(ans==-1)  puts("Poor QLW");
		else  printf("%d\n",getcnt(ans)-(getsum(ans)-k)/ans);
	}
}
struct node
{
	int l,r,sum,cnt;
}e[N*40];
int rt[N],tot,a[N];
void insert(int &p,int pre,int l,int r,int pos)
{
	e[++tot]=e[pre];
	p=tot;
	e[p].cnt++;
	e[p].sum+=pos;
	if(l==r)  return;
	int mid=(l+r)>>1;
	if(pos<=mid)  insert(e[p].l,e[pre].l,l,mid,pos);
	else  insert(e[p].r,e[pre].r,mid+1,r,pos);
}
int ask(int L,int R,int l,int r,int k)
{
	if(l==r)  return (k-1)/l+1;
	int mid=(l+r)>>1;
	int cnt=e[e[R].r].cnt-e[e[L].r].cnt;
	int sum=e[e[R].r].sum-e[e[L].r].sum;
	if(sum<k)  return cnt+ask(e[L].l,e[R].l,l,mid,k-sum);
	return ask(e[L].r,e[R].r,mid+1,r,k);
}
void solve1()
{
	for(re i=1;i<=m;i++)  scanf("%d",&a[i]);
	for(re i=1;i<=m;i++)  insert(rt[i],rt[i-1],1,1000,a[i]);
	while(q--)
	{
		int k;
		scanf("%d%d%d%d%d",&a1,&b1,&a2,&b2,&k);
		if(e[rt[b2]].sum-e[rt[b1-1]].sum<k)  puts("Poor QLW");
		else  printf("%d\n",ask(rt[b1-1],rt[b2],1,1000,k));
	}
}
void solve()
{
	cin>>n>>m>>q;
	if(n==1)  solve1();
	else  solve2();
}
signed main()
{
    int T=1;
//    cin>>T;
    for(int index=1;index<=T;index++)
    {
//    	printf("Case %lld:\n",index);
        solve();
//        puts("");
    }
    return 0;
}
/*







*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值