题解:P11203 [JOIG 2024] 感染シミュレーション / Infection Simulation
传送门
我的博客,以享受更好的阅读体验。
题目意思
题目其实很简洁了可以直接看,这里解释一下
如果一个人会被另一个人感染,当且仅当他们同时存在的时间区间
思考过程
这里提供思考过程,希望直接知道怎么做的可以直接看下面的做法部分。
首先题目给出了
第二步,观察题目性质,发现很难入手,没有什么切入点,那么考虑暴力,暴力做法是将所有现在能被感染的人设为感染状态,然后不停的扩散直到无法感染为止,所以时间复杂度是
考虑将感染过程优化,将一个询问中一个人向他能感染的人连边,如下图(读者可以自行从一堆样例里面随便选一个手玩)。
不仅如此,手玩多次之后会发现一个很有趣的现象,我们的感染路径从开始位置一直走可以找到一条链,使得这个链经过所有节点,比如上图,可以找到
所以可以预处理每一个节点下一次传染的人是谁,接着他的传染路径可以使用倍增优化,关于
所以就做完了……吗。
会发现一个问题,如果我们现在有一个点在餐厅的时间非常短,可能只有一分钟,那么这个人铁定不会被感染,但是我们的倍增可能会把他算进来,而且如果一个人在感染源进来之前就进来了,也会算不到这个人的贡献。
考虑怎么处理这个问题,我们这个链的开头和结尾有什么性质,发现这个链的所有节点涵盖了所有感染者的时间,所以我们相当于找到了感染时间的左端点和右端点。
接下来的就好做了,我们只需要找到所有右端点在感染源右端的所有点,并且找到和感染区间交集超过
做法
按照
代码
//上面如果讲的不够清楚可以看这里
#include<algorithm>
#include<iostream>
#include<cstring>
#include<climits>
#include<cmath>
#define ll long long
using namespace std;
const ll N=2e5+9;
struct person{ll l,r,id;}a[N],c[N];
struct query{ll l,r,mn,id;}que[N];
ll nxt[N][25],lg[N],g[N][20];
ll n,q,book[N],ans[N];
inline bool cmp(person x,person y){
return x.r<y.r;
}
inline bool cmpp(person x,person y){
return (x.r-x.l)>(y.r-y.l);
}
inline bool Cmp(query x,query y){
return x.mn>y.mn;
}
struct seg{
#define lowbit(x) (x&(-x))
int tr[N];
inline void change(int x,int y){
for(;x<=n;x+=lowbit(x))
tr[x]+=y;
}
inline int query(int x){
int sum=0;
for(;x;x-=lowbit(x)){
sum+=tr[x];
}
return sum;
}
}seg;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].l>>a[i].r,a[i].id=i;
sort(a+1,a+n+1,cmp);
ll mn=INT_MAX,pos=0;
for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=n;i>=1;i--){
nxt[i][0]=0;
g[i][0]=INT_MIN;
if(i!=n){
nxt[i][0]=pos;
g[i][0]=a[i].r-max(a[i].l,a[pos].l);
}
if(a[i].l<mn){
mn=a[i].l;
pos=i;
}
}
for(int i=1;i<=lg[n];i++)
for(int j=1;j<=n;j++){
nxt[j][i]=nxt[nxt[j][i-1]][i-1];
g[j][i]=min(g[j][i-1],g[nxt[j][i-1]][i-1]);
}
for(int i=1;i<=n;i++)book[a[i].id]=i;
cin>>q;
for(int i=1;i<=q;i++){
int p,x;
cin>>p>>x;
que[i].l=a[book[p]].l;
p=book[p];
for(int j=lg[n];j>=0;j--)
if(x<=g[p][j])
p=nxt[p][j];
que[i].r=a[p].r;
que[i].mn=x;
que[i].id=i;
}
for(int i=1;i<=n;i++)
c[i]=a[i],c[i].id=i;
sort(c+1,c+n+1,cmpp);
sort(que+1,que+q+1,Cmp);
ll now=1;
for(int i=1;i<=q;i++){
while((c[now].r-c[now].l)>=que[i].mn && now<=n)
seg.change(c[now].id,1),now+=1;
int lt=1,rt=n,ans1=-1;
while(lt<=rt){
int mid=lt+rt>>1;
if(a[mid].r>=que[i].l+que[i].mn) ans1=mid,rt=mid-1;
else lt=mid+1;
}
int ans2=-1;
lt=1;rt=n;
while(lt<=rt){
int mid=lt+rt>>1;
if(a[mid].r<=que[i].r) ans2=mid,lt=mid+1;
else rt=mid-1;
}
if(ans1==-1 || ans2==-1 || ans1>ans2){ans[que[i].id]=1;continue;}
ans[que[i].id]=seg.query(max(ans2,0))-seg.query(max(ans1-1,0));
if(ans[que[i].id]<1) ans[que[i].id]=1;
}
for(int i=1;i<=q;i++)
cout<<ans[i]<<endl;
return 0;
}