样例输入
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;
}