「BZOJ1067」降雨量【动态开点线段树或RMQ】

5 篇文章 0 订阅
3 篇文章 0 订阅

题目链接:传送门

  • 1067: [SCOI2007]降雨量

Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 7371 Solved: 1980
[Submit][Status][Discuss]

Description

我们常常会说这样的话:“ X X X年是自 Y Y Y年以来降雨量最多的”。它的含义是X年的降雨量不超过 Y Y Y年,且对于任意
Y < Z < X , Z Y<Z<X,Z YZXZ年的降雨量严格小于 X X X年。例如 2002 , 2003 , 2004 2002,2003,2004 200220032004 2005 2005 2005年的降雨量分别为 4920 , 5901 , 2832 4920,5901,2832 492059012832 3890 3890 3890
则可以说“ 2005 2005 2005年是自 2003 2003 2003年以来最多的”,但不能说“ 2005 2005 2005年是自 2002 2002 2002年以来最多的”由于有些年份的降雨量未
知,有的说法是可能正确也可以不正确的。

Input

输入仅一行包含一个正整数 n n n,为已知的数据。以下 n n n行每行两个整数 y i y_i yi r i r_i ri,为年份和降雨量,按照年份从小
到大排列,即 y i < y i + 1 y_i<y_{i+1} yiyi+1。下一行包含一个正整数 m m m,为询问的次数。以下 m m m行每行包含两个数 Y Y Y X X X,即询问“ X X X年是
Y Y Y年以来降雨量最多的。”这句话是必真、必假还是“有可能”。

Output

对于每一个询问,输出true,false或者maybe。

Sample Input

6
2002 4920
2003 5901
2004 2832
2005 3890
2007 5609
2008 3024
5
2002 2005
2003 2005
2002 2007
2003 2007
2005 2008

Sample Output

false
true
false
maybe
false

HINT

100%的数据满足: 1 &lt; = n &lt; = 50000 , 1 &lt; = m &lt; = 10000 , − 1 0 9 &lt; = y i &lt; = 1 0 9 , 1 &lt; = r i &lt; = 1 0 9 1&lt;=n&lt;=50000, 1&lt;=m&lt;=10000, -10^9&lt;=y_i&lt;=10^9, 1&lt;=r_i&lt;=10^9 1<=n<=50000,1<=m<=10000,109<=yi<=109,1<=ri<=109

Source

POJ 2637 WorstWeather Ever

  • 题解:动态开点线段树或RMQ

    • 考验逻辑能力好题,注意!!!心脏不好的同学不要去做这道题233
    • 显然分类讨论题,讨论如下:
      • 如果 X X X Y Y Y年雨量都知道:
        • 如果 X X X年降雨量小于等于 Y Y Y年降雨量,继续分类
          • 如果 Y Y Y X X X 之间的所有年份降雨量数据都知道且最大降雨量 &lt; &lt; < X X X年的降雨量,则为 t r u e true true;
          • 如果 Y Y Y X X X之间有部分年份降雨量知道且其中最大降雨量 &gt; = X &gt;= X >=X年降雨量,则为 f a l s e false false
          • 否则为 m a y b e maybe maybe
        • 否则 f a l s e false false
      • 如果 Y Y Y年降雨量知道 X X X年不知道
        • 如果 Y Y Y X X X 之间有一些年份数据知道且最大值 &gt; = Y &gt;=Y >=Y年降雨量,则为 f a l s e false false
        • 否则 m a y b e maybe maybe
      • 如果 X X X年降雨量知道 Y Y Y年不知道
        -如果 X X X Y Y Y 之间有一些年份数据知道且最大值 &gt; = X &gt;=X >=X年降雨量,则为 f a l s e false false
        • 否则 m a y b e maybe maybe
      • X X X Y Y Y 都不知道 ⇒ \Rightarrow m a y b e maybe maybe
    • 显然维护区间最值然后分类大讨论就行了,首先普通线段树的话肯定是不行的, 因为这个需要 n l o g n nlogn nlogn的空间。。实际上并不是每个节点都有信息,也就是说,只有零星的最多50000个点需要维护,所以考虑没用的节点就不去申请空间了,因为每个点从根节点维护下来最多需要 log ⁡ 2 50000 \log_2{50000} log250000个节点,加起来一共就是 n log ⁡ n n\log n nlogn的空间,所以能满足要求。
    • 我的代码其实还可以继续优化,我对所有数据建区间 [ − 1 0 9 , 1 0 9 ] [-10^9,10^9] [109,109]的线段树,实际上可以建 [ m i n _ y e a r , m a x _ y e a r ] [min\_year,max\_year] [min_year,max_year]的就行了,然后端点查询的时候我是直接在建的树上进行单点查询,然而可以先维护一个 m a p map map,然后查询的话会使常数小一些。
    • 当然这题也可以 m a p + R M Q map+RMQ map+RMQ搞搞,而且比这个好写很多,博主只是闲的蛋疼,硬生生套上一颗线段树 233 233 233
    • 另外第一次被 l o n g   l o n g long\ long long long卡,知道 i n t ∗ 1 L L int*1LL int1LL的用处了
    • 两种方法对比一下:(第一个是RMQ 写的,对二个是线段树 )
      在这里插入图片描述
  • 动态开点线段树(模版)

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=50000+10;

struct dynamic_segment_tree{
	int tot;
	struct node{
		int maxx,mark;
		int ls,rs,sum;
		node(int a=0,int b=0,int c=0,int d=0,int e=0){
			maxx=a;sum=b;mark=c;ls=d;rs=e;
		}
	}tree[20*maxn];

	dynamic_segment_tree(){
		tot=1;
		memset(tree,0,sizeof(tree));
		tree[0].maxx=-2e9;
	}
	void init(){
		tot=1;
		memset(tree,0,sizeof(tree));
		tree[0].maxx=-2e9;
	}

	void pushup(int id){
		tree[id].maxx=max(tree[tree[id].ls].maxx,tree[tree[id].rs].maxx);
		tree[id].sum=tree[tree[id].ls].sum+tree[tree[id].rs].sum;
	}

	//单点更新
	void update(int l,int r,int k,int val,int id){
		if(l==r){
			tree[id].maxx=val;
			tree[id].sum=1;
			return;
		}

		int mid=(l+r)>>1;
		if(k<=mid){
			if(!tree[id].ls) tree[id].ls=++tot;
			update(l,mid,k,val,tree[id].ls);
		}else{
			if(!tree[id].rs) tree[id].rs=++tot;
			update(mid+1,r,k,val,tree[id].rs);
		}
		pushup(id);
	}

	int query(int l,int r,int le,int ri,int id){ //l,r区间端点,le,ri待查询区间
		if(le>ri) return -2e9;
		int ans=-2e9;
		if(l>=le&&r<=ri) return tree[id].maxx;

		int mid=(l+r)>>1;
		if(le<=mid&&tree[id].ls) ans=max(ans,query(l,mid,le,ri,tree[id].ls));
		if(ri>mid&&tree[id].rs) ans=max(ans,query(mid+1,r,le,ri,tree[id].rs));
		return ans;
	}

	int sum(int l,int r,int le,int ri,int id){
		if(le>ri) return 0;
		int ans=0;
		if(le<=l&&ri>=r) return tree[id].sum;
		int mid=(l+r)>>1;
		if(le<=mid&&tree[id].ls) ans+=sum(l,mid,le,ri,tree[id].ls);
		if(ri>mid&&tree[id].rs) ans+=sum(mid+1,r,le,ri,tree[id].rs);
		return ans;	
	}
}data;

int n,m;
int a,b,year,water,L=-1e9,R=1e9;

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&year,&water);
		data.update(L,R,year,water,1);
	}

	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d %d",&a,&b);
		if(b<a) {printf("false\n");continue;}

		int le=data.sum(L,R,a,a,1),ri=data.sum(L,R,b,b,1),mid=data.sum(L,R,a+1,b-1,1);
		int c=data.query(L,R,a,a,1),d=data.query(L,R,b,b,1),e=data.query(L,R,a+1,b-1,1);
		if(le&&ri){
			if(d<=c){
				if(mid==b-a-1&&e<d) printf("true\n");
				else if(e<d) printf("maybe\n");
				else printf("false\n"); 
			}else printf("false\n");
		}else if(le){
			if(e<c) printf("maybe\n");
			else printf("false\n");
		}else if(ri){
			if(e<d) printf("maybe\n");
			else printf("false\n");
		}else printf("maybe\n");
	}
}
  • RMQ

#include<bits/stdc++.h>

using namespace std;
const int maxn=50000+10;

int n,q,a[maxn],dp[maxn][20],year[maxn],l,r;
map<int,int>mp; 

void rmq()
{
	for(int i=1;i<=n;i++) dp[i][0]=a[i];
	for(int j=1;(1<<j)<=n;j++){
		for(int i=1;(i+(1<<j)-1)<=n;i++){
			dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
		}
	}
}

int query(int le,int ri)
{
	if(le>ri) return 0;
	int k=0;
	while(le+(1<<(k+1))-1<=ri) k++;
	return max(dp[le][k],dp[ri-(1<<k)+1][k]);
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&year[i],&a[i]);
		mp[year[i]]=i;
	}
	rmq();scanf("%d",&q);
	while(q--){
		scanf("%d %d",&l,&r);
		int idl=mp[l],idr=mp[r];
		if(idl&&idr){
			int k=query(idl+1,idr-1);
			if(idr-idl==r-l&&k<a[idr]&&a[idr]<=a[idl]) puts("true");
			else if(idr-idl<r-l&&k<a[idr]&&a[idr]<=a[idl]) puts("maybe");
			else puts("false");
		}else if(idl&&!idr){
			int id=lower_bound(year+1,year+n+1,r)-year-1;
			int k=query(idl+1,id);
			if(k>=a[idl]) puts("false");
			else puts("maybe");
		}else if(!idl&&idr){
			int id=lower_bound(year+1,year+n+1,l)-year;
			int k=query(id,idr-1);
			if(a[idr]>k) puts("maybe");
			else puts("false");
		}else{
			puts("maybe");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值