感染シミュレーション / Infection Simulation JOIG 2024

[JOIG 2024] 感染シミュレーション / Infection Simulation

有 nn 个顾客,第 ii 的顾客存在时间为 [li,ri][l_i,r_i]。

如果一个顾客 ii 存在的时间中,与被感染的顾客一起存在了 ≥x\ge x 的时间,那么顾客 ii 也会被感染。

多次询问,每次给定 p,xp,x,求如果 pp 顾客被感染了,且给定感染系数为 xx,有多少个顾客会被感染。

n≤105n\le 10^5。

子任务:p=1p=1,且 l,rl,r 随 ii 单调不减。

要想充要的转化。

先看子任务。

此时顾客 11 会最先被感染。然后根据 l,rl,r 单调不降,感染顺序一定是 1,2,⋯1,2,\cdots。那么只需要满足相邻两个能传染即可,可以使用 ST 表 O(log⁡n)\mathcal{O}(\log n) 的查询。

接着看正解。

首先我们肯定要将 ll 为第一关键字,rr 会第二关键字排序。接下来讨论的下标均为排序后的下标。

引理 1:

存在顾客被感染的时间必然连续。

证明:

考虑第一个被 pp 感染的人,其感染前必然存在感染的人 pp,其感染后必然其出现的所有时间都有人感染。

因此归纳可证感染时间必然连续。

通过子任务也可以轻松观察出来。

接下来考虑 i≠pi\ne p 感染的充分条件是什么。先提前特判 rp−lp≥xr_p-l_p\ge x。

如果说 i<pi<p,那么条件肯定是 lp+x≤ril_p+x\le r_i。因此这一部分可以直接树桩数组扫描线解决。

接下来看 i>pi>p 的部分。

首先如果 ii 被感染,那么必然有 ri−li≥xr_i-l_i\ge x,不然 ii 就没有足够的时间被传染了。

然后 ii 必须和感染的人在一起。因此考虑 j<ij<i 且 jj 被感染,那么 jj 能感染 ii 的充要条件是 rj−li≥xr_j-l_i\ge x。

最后,jj 由于 j<ij<i,那么 lj≤lil_j\le l_i,因此我只要取 rjr_j 最大的 jj 即可。因为引理 1 可知有感染者的时间连续,那么既然 ii 需要被感染,那么前面的时间都应该被感染,也就是说,位于 p≤j<ip\le j<i 的 jj,只要 rj−lj≥xr_j-l_j\ge x 则必然被感染。又因为 jj 为 rjr_j 最大的,于是如果不满足 rj−lj≥xr_j-l_j\ge x 则 rj−li≥xr_j-l_i\ge x 也不成立。于是这个转化就是充要的。

定义 RiR_i 表示 max⁡j<i{rj}\max\limits_{j<i}\{r_j\},那么 ii 被感染的充要条件为:

ri−li≥x∧Ri−li≥xr_i-l_i\ge x\land R_i-l_i\ge x

注意引理 1,感染的区间需要连续。因此 ii 被感染还需要对于任意的 p<j<ip<j<i,有 Rj−lj≥xR_j-l_j\ge x。

接下来我们只要处理这个即可。

套路的枚举 xx 扫描线。使用树桩数组维护所有顾客是否满足 ri−li≥xr_i-l_i\ge x,同时使用 set 记录所有不满足 Ri−li≥xR_i-l_i\ge x 的 ii。

那么每次询问时只需要 upper_bound 第一个 >i>i 的数 tt,答案就累加 (p,t)(p,t) 中 ri−li≥xr_i-l_i\ge x 的数的个数。

最后加上 pp 自己即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <bits/stdc++.h>
using namespace std;
const int N=400010;
int ans[N],n,m,L[N],R[N],am[N],ss,c[N],p[N],np[N],R2[N],len[N];
struct node{int x,y;}q[N];
int get(int x){return lower_bound(am+1,am+1+ss,x)-am;}
int nn;
void clear(int n){nn=n;for(int i=1;i<=n;i++)c[i]=0;}
void add(int x,int y){for(int i=x;i<=nn;i+=(i&-i))c[i]+=y;}
int query(int x){int cnt=0;for(int i=x;i>=1;i-=(i&-i))cnt+=c[i];return cnt;}
int query(int l,int r){return l>r?0:query(r)-query(l-1);}
vector<node>v[N],V[N];
set<int>S;
using pii=pair<int,int>;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",L+i,R+i),am[++ss]=L[i],am[++ss]=R[i],len[i]=R[i]-L[i];
sort(am+1,am+1+ss);ss=unique(am+1,am+1+ss)-am-1;
for(int i=1;i<=n;i++)L[i]=get(L[i]),R[i]=get(R[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d%d",&q[i].x,&q[i].y);
for(int i=1;i<=n;i++)p[i]=i;
sort(p+1,p+1+n,[](int x,int y){return L[x]==L[y]?R[x]<R[y]:L[x]<L[y];});
for(int i=1;i<=n;i++)np[p[i]]=i;
for(int i=1;i<=m;i++)if(len[q[i].x]>=q[i].y)
V[np[q[i].x]-1].push_back({get(am[L[q[i].x]]+q[i].y),i});
for(int i=1;i<=n;i++)v[np[i]].push_back({R[i],1});
clear(ss);
for(int i=1;i<=n;i++){
for(node j:v[i])add(j.x,j.y);
for(node j:V[i])ans[j.y]+=query(j.x,ss);
}
priority_queue<pii,vector<pii>,greater<pii>>q1,q2;
R2[1]=-1;
for(int i=1;i<n;i++)R2[i+1]=max(R2[i],R[p[i]]);
for(int i=1;i<=n;i++){
q1.push({am[R[i]]-am[L[i]],np[i]});
q2.push({am[R2[np[i]]]-am[L[i]],np[i]});
}
ss=0;
for(int i=1;i<=m;i++)am[++ss]=q[i].y;
sort(am+1,am+1+ss);ss=unique(am+1,am+1+ss)-am-1;
for(int i=1;i<=ss;i++)v[i].clear();
for(int i=1;i<=m;i++)if(len[q[i].x]>=q[i].y)
v[get(q[i].y)].push_back({q[i].x,i});
S.insert(n+1);
clear(n);
for(int i=1;i<=n;i++)add(i,1);
for(int i=1;i<=ss;i++){
while(!q1.empty()){
pii t=q1.top();
int x=t.second,y=t.first;
if(y>=am[i])break;
q1.pop();
add(x,-1);
}
while(!q2.empty()){
pii t=q2.top();
int x=t.second,y=t.first;
if(y>=am[i])break;
q2.pop();
S.insert(x);
}
for(node j:v[i]){
int x=j.x,id=j.y;
int t=*S.upper_bound(np[x]);
ans[id]+=query(np[x]+1,t-1);
}
}
for(int i=1;i<=m;i++)ans[i]++;
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}