CCF CSP认证 2022-03-2出行计划 详解——带你从零解题

样例输入

6 2 10
5 24
10 24
11 24
34 24
35 24
35 48
1
2

Data

样例输出

3
3

Data

样例解释

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

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

子任务

40% 的测试数据满足1000个、m=1;

70% 的测试数据满足都是1000;

全部的测试数据满足105。

解题思路

我们先用正常的想法去做,设start为开始的时间,那么只需要我们从这个start后每一个查询每一个事件是否能囊括在start+t,t就是需要预留的时间。

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int maxn = 200000+10;
int a[maxn];
int b[maxn];
int t[maxn];

int main()
{
	cin>>n>>m>>k;
	for(int i=0;i<n;i++)
	{
		cin>>a[i]>>b[i];
	}
	for(int i=0;i<m;i++)
	{
		cin>>t[i];
	}
	for(int i=0;i<m;i++)
	{
		int ans=0;
		int start = t[i]+k;
		for(int j=0;j<n;j++)
		{
			if(start<=a[j]&&start+b[j]>a[j])ans++;
		}
		
		cout<<ans;
		if(i!=m-1)
		cout<<endl;
	}
	return 0;
} 

超时了很好,

我们看一下n,m,k在100000的条件下,就需要查询10亿次,大概需要5秒肯定是不行

为什么会多一层循环呢,是因为我们需要从每一个所给的时刻开始从新计算,那我们就有这么一个想法——我们先用一个循环把1-无限的时刻能够包含的事件算出来,那么当我们查询的时候就可以直接拿来用,这样就可以减少一层循环

那我们也不需要数组了

想法可行,实践开始

我们对每一个事件进行操作,对每一个时间来说,只有一段时间可以参与进来,那么我们就把这段时间的每一个时间点可以进行的事件数都加一就可以了

for(int i=0;i<n;i++)
	{
		cin>>a>>b;
		int Min = a-b-k+1;
		Min = max(Min,0);//正数
		int Max = max(a-k,0);
		
		
	}

有了min,max的时间限制,只需要在这段时间里事件数加一即可,但是我总不能加个循环都加一吧,那不就又成了O(n*n),虽然可能时间会减少一点

        ans[Min]++;
        ans[Max+1]--;

只需要在后面加上这两句就算是记录完成了,++好说,这--是为什么?

我们在这里用到差分的思想,例如我要寻找ans[n],n这个时间是所有事件的中间的点,那么他的值应该是0(不算ans这两句),而他真正的值应该是他前面所有ans的和,ans[n] = ans[n-1]+..+..+..ans[0]。

但是,问题就来了,假如n这个时间点所有的事件都不能参加,数量就是0,所以我们要在一个时间的边界Max+1 的时间把n的这个给减去,取消掉,加的时候就不会多加。

for(int i=1;i<200001;i++)
	{
		ans[i]+=ans[i-1];
	}
	for(int i=0;i<m;i++)
	{
		cin>>t[i];
	}
	for(int i=0;i<m;i++)
	{
		cout<<ans[t[i]];
		if(i!=m-1)cout<<endl;
	}

ans[]累加,以前缀和的形式,避免二重循环。

最后直接根据输入输出ans就行了

最后一个if判断防止最后一个换行导致结果不对,血的教训啊

【全部代码】

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int maxn = 200000+10;
int a;
int b;
int t[maxn];
int ans[maxn];

int main()
{
	cin>>n>>m>>k;
	for(int i=0;i<n;i++)
	{
		cin>>a>>b;
		int Min = a-b-k+1;
		Min = max(Min,0);//正数
		int Max = max(a-k,0);
		ans[Min]++;
		ans[Max+1]--;
	}
	for(int i=1;i<200001;i++)
	{
		ans[i]+=ans[i-1];
	}
	for(int i=0;i<m;i++)
	{
		cin>>t[i];
	}
	for(int i=0;i<m;i++)
	{
		cout<<ans[t[i]];
		if(i!=m-1)cout<<endl;
	}
	
	return 0;
} 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值