CSP认证 202203-2 出行计划(多种解法)

 

解法一前置知识:差分数组

解法二前置知识:线段树

试题编号:202203-2
试题名称:出行计划
时间限制:1.5s
内存限制:512.0MB
问题描述:

问题描述

最近西西艾弗岛上出入各个场所都要持有一定时限内的核酸检测阴性证明。

具体来时,如果在 t 时刻做了核酸检测,则经过一段时间后可以得到核酸检测阴性证明。这里我们假定等待核酸检测结果需要 k 个单位时间,即在 t+k 时刻可以获得结果。如果一个场所要求持 24 个单位时间内核酸检测结果入内,那么凭上述的核酸检测结果,可以在第 t+k 时刻到第 t+k+23 时刻进入该场所。

小 C 按时间顺序列出接下来的 n 项出行计划,其中第 i 项(1≤i≤n)可以概括为:
ti 时刻进入某场所,该场所需持有 ci 个单位时间内的核酸检测结果入内,其中 0<ci≤2×105。

为了合理安排核酸检测时间,试根据小 C 的出行计划,回答如下查询:

  • 如果在 q 时刻做了核酸检测,有多少项出行计划的核酸检测要求可以得到满足?

这样的查询共有 m 个,分别为 q1,q2,⋯,qm;查询之间互不影响。

输入格式

输入的第一行包含空格分隔的三个正整数 n、m 和 k,分别表示出行计划数目、查询个数以及等待核酸检测结果所需时间。

接下来输入 n 行,其中每行包含用空格分隔的两个正整数 ti、ci,表示一项出行计划;出行计划按时间顺序给出,满足 0<t1≤t2≤⋯≤tn≤2×105。

最后输入 m 行,每行仅包含一个正整数 qi,表示一个查询。m 个查询亦按照时间顺序给出,满足 0<q1<q2<⋯<qm≤2×105。

输出格式

输出共 m 行,每行一个整数,表示对应查询的答案。

样例输入

6 2 10

5 24

10 24

11 24

34 24

35 24

35 48

1
2

Data

样例输出

3
3

Data

样例解释

时刻 1 做检测,可以满足第三、四、六项出行计划;

时刻 2 做检测,可以满足第四、五、六项出行计划。

子任务

40% 的测试数据满足 0<n,k≤1000、m=1;

70% 的测试数据满足 0<n,m,k≤1000;

全部的测试数据满足 0<n,m,k≤105。

 

        题目本质:每个出行计划都代表一个区间。每次查询就是查询当前时间x在k时间后(即x+k)被多少个区间包含。

解法一:差分数组(最好想、码量最少的解法)

        差分数组适合于多次区间修改,极少区间查询或者先区间修改再区间查询,不是边修改边查询的情况。本题使用差分数组,每个出行计划就是对有效区间的操作。最后前缀和统计一下,随后查询即可。

/*
差分数组版本,五分钟就AC,比线段树好太多了 
*/

#include <cstdio>
#define fi(i,l,r) for(int i=(l);i<=(r);++i)
#define fd(i,r,l) for(int i=(r);i>=(l);--i)

using namespace std;

int n,m,t;
int x,y;
int a[300005];

template<typename T>void Read(T &x)
{
	x=0;T k=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') k=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	x*=k;
}
template<typename T>void Print(T x)
{
	if(x<0){putchar('-');Print(-x);return;}//这个return很重要,某些情况可能出错
	if(x>9) Print(x/10);
	putchar((x%10)^48);
}

int main (void)
{
	Read(n);
	Read(m);
	Read(t);
	while(n--)
	{
		Read(x);
		Read(y);
		a[x-y<0?1:x-y+1]++;
		a[x+1]--;
	}
	fi(i,1,300004)
	  a[i]+=a[i-1];
	while(m--)
	{
		Read(x);
		Print(a[x+t]);
		putchar('\n');
	}
	return 0;
}

解法二:线段树(码量较多)

        线段树区间修改和区间查询都在log级别,也是符合要求的。本题只需要写单点查询就行,所以和一般的线段树模板有一定的区别。

#include <cstdio>
#define fi(i,l,r) for(int i=(l);i<=(r);++i)
#define fd(i,r,l) for(int i=(r);i>=(l);--i)
#define lc (rt<<1)
#define rc ((rt<<1)|1)
#define mid ((R+L)>>1)

using namespace std;
const int N=300005;

int n,m,t;
int x,y;
struct arr{
	int add,val;
}str[N<<2];//线段树一般要开到四倍大 

template<typename T>void Read(T &x)
{
	x=0;T k=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') k=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	x*=k;
}
template<typename T>void Print(T x)
{
	if(x<0){putchar('-');Print(-x);return;}//这个return很重要,某些情况可能出错
	if(x>9) Print(x/10);
	putchar((x%10)^48);
}

void pushUp(int rt)
{
	str[rt].val=str[lc].val+str[rc].val;
}

void pushDown(int rt,int L,int R)
{
	str[lc].val+=str[rt].add*(mid-L+1);
	str[rc].val+=str[rt].add*(R-mid);
	str[lc].add+=str[rt].add;
	str[rc].add+=str[rt].add;
	str[rt].add=0;
}

void Upd(int rt,int L,int R,int ul,int ur,int addVal)
{
	if(L>ur||R<ul) return;
	if(ul<=L&&ur>=R)
	{
		str[rt].add+=addVal;
		str[rt].val+=addVal*(R-L+1);
		return;
	}
	if(str[rt].add!=0) pushDown(rt,L,R);
	if(ul<=mid) Upd(lc,L,mid,ul,ur,addVal);
	if(ur>mid) Upd(rc,mid+1,R,ul,ur,addVal);
	pushUp(rt);
}

int Query(int rt,int L,int R,int x)
{
	if(L==x&&R==x) return str[rt].val;
	if(str[rt].add!=0) {pushDown(rt,L,R);pushUp(rt);}
	if(x<=mid) return Query(lc,L,mid,x);
	else return Query(rc,mid+1,R,x);
}

int main (void)
{
	Read(n);
	Read(m);
	Read(t);
	while(n--)
	{
		Read(x);
		Read(y);
		Upd(1,1,300000,x-y<0?1:x-y+1,x,1);
	}
	while(m--)
	{
		Read(x);
		Print(Query(1,1,300000,x+t));
		putchar(10);
	}
	return 0;
}


 

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重理工小菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值