ABC 305D Sleep Log
题意
- 现在给你一个高桥君的睡眠表a,若i为奇数,那么高桥君在a[i]~a[i+1]这段时间没有睡觉,反之则处于睡眠期间。现在有q次询问,每次询问会给出l,r,分别表示起始时间和终止时间,求这段时间内高桥君的睡眠时间
思路
- 我们可以将每个[l,r]拆成若干个整块的睡眠时间或未睡眠时期加上零碎的睡眠时间或未睡眠时间。类似于分块的想法,整块我们统一前缀和处理,而碎块单独处理即可。
- 这里我们遇到的问题是:
- 如何表示一个区间,并判断是否为睡眠时间。
- 需要对所有的睡眠时间单独拎出来进行前缀和,否则会tle。
- 对于第一个问题我们可以采取用map储存一个数对对于一个数的映射,数对存储一个区间的左端点和右端点,而那一个数就是我们对块的标记的序号,若为奇数,则为未睡眠时期,反之为睡眠时期。
- 对于第二个问题,我们单独开一个数组即可,然后我们可以很明显的发现,在原数组睡眠时间段的序号对应的单独拎出来的数组里的序号是两倍关系,这样我们就能很快的使用前缀和了。
- 想补充的一点是,注意一下端点的情况,具体可以看码。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
map<pair<int,int>,int>ds;
map<int,pair<int,int>>ddd;
const int maxn=2e5+10;
int a[maxn],slp[maxn],sum[maxn],cnt;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n-1;i++)
{
ds[make_pair(a[i],a[i+1])]=i+1;
ddd[i+1]=make_pair(a[i],a[i+1]);
}
int flag=1;
for(int i=0;i<n-1;i++)
{
if(flag%2==0) slp[++cnt]=a[i+1]-a[i];
flag++;
}
for(int i=1;i<=cnt;i++) sum[i]=sum[i-1]+slp[i];
int q;
cin>>q;
while(q--)
{
int ans=0;
int l,r;
cin>>l>>r;
int flag1=lower_bound(a,a+n,l)-a;
int d1=ds[make_pair(a[flag1-1],a[flag1])];
int flag2=lower_bound(a,a+n,r)-a;
int d2=ds[make_pair(a[flag2-1],a[flag2])];
//for(int i=d1+1;i<=d2-1;i++) if(i%2==0) ans+=ddd[i].second-ddd[i].first;
int begin=d1+1;
int end=d2-1;
if(begin%2==1) begin++;
if(end%2==1) end--;
ans+=sum[end/2]-sum[begin/2-1];
if(d1%2==0) ans+=a[flag1]-l;
if(d2%2==0) ans+=r-a[flag2-1];
cout<<ans<<endl;
}
return 0;
}