P14255 列车(train)题解
出题人题解。
解法 1
暴力维护每趟列车的状态,查询暴力枚举每趟列车。
时间复杂度
解法 2
下文以列车
若列车
若列车
也就是说如果固定起点站/终点站,列车是否被停开关于它的终点站/起点站具有单调性。所以我们可以对于每个
停开事件相当于对于
时间复杂度
解法 3
对于每次停开事件令
对于查询事件,考虑列车
因为当
时间复杂度
解法 4
考虑查询时不枚举
对于贡献为第 1 类或第 3 类的列车
时间复杂度
解法 5
因为后面的停开事件的列车范围是包含前面的停开事件的,因此查询事件只需要考虑在此之前最后一次停开事件即可。
时间复杂度
解法 6
考虑去掉列车范围被包含的停开事件,可以使用珂朵莉树在线维护范围互不包含的列车
第 1 类列车和第 3 类列车可以根据解法 4 中的结论快速找到对应的唯一的列车,第 2 类列车可以暴力枚举贡献。
当
时间复杂度
解法 7
考虑使用线段树维护
可以通过线段树上二分找出解法 3 中三类列车的分界点,在线段树上分别查询区间
时间复杂度
这个题还有别的做法,例如 ODT+线段树、分块等,不过这些都是我一开始没有预料到的。
对于本题被卡常数的选手我感到抱歉,一个方面是 cz 说下发文件不能太大,因此 但是如果平方过题就成追忆了。
有人没发现
::::success[我的代码]
#include<iostream>
using namespace std;
#define N 100010
int t,n,m,p[N];
class sgtree
{
public:
#define sN 400010
int mi[sN],ma[sN],val[sN],tag[sN];
void build(int o,int l,int r)
{
tag[o]=1000000000;
if(l==r)
{
mi[o]=ma[o]=p[l];
val[o]=0;
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
mi[o]=min(mi[o<<1],mi[o<<1|1]);
ma[o]=max(ma[o<<1],ma[o<<1|1]);
val[o]=min(val[o<<1],val[o<<1|1]);
}
void pushdown(int o,int l,int r)
{
if(tag[o]>=1000000000)
return;
int mid=l+r>>1;
tag[o<<1]=tag[o];
mi[o<<1]=ma[o<<1]=tag[o];
val[o<<1]=p[l]-tag[o];
tag[o<<1|1]=tag[o];
mi[o<<1|1]=ma[o<<1|1]=tag[o];
val[o<<1|1]=p[mid+1]-tag[o];
tag[o]=1000000000;
}
void update(int o,int l,int r,int x,int y,int k)
{
if(l>y||r<x)
return;
if(k>=ma[o])
return;
if(l>=x&&r<=y&&k<=mi[o])
{
tag[o]=k;
mi[o]=ma[o]=k;
val[o]=p[l]-k;
return;
}
pushdown(o,l,r);
int mid=l+r>>1;
update(o<<1,l,mid,x,y,k);
update(o<<1|1,mid+1,r,x,y,k);
mi[o]=min(mi[o<<1],mi[o<<1|1]);
ma[o]=max(ma[o<<1],ma[o<<1|1]);
val[o]=min(val[o<<1],val[o<<1|1]);
}
int querybound(int k,int n)
{
if(ma[1]<=k)
return n;
int o=1,l=1,r=n,ans=0;
while(l<r)
{
pushdown(o,l,r);
int mid=l+r>>1;
if(ma[o<<1]<=k)
{
l=mid+1;
o=(o<<1|1);
}
else
{
r=mid;
o=(o<<1);
}
}
return l-1;
}
int queryone(int o,int l,int r,int x)
{
if(l==r)
return mi[o];
pushdown(o,l,r);
int mid=l+r>>1;
if(x<=mid)
return queryone(o<<1,l,mid,x);
else
return queryone(o<<1|1,mid+1,r,x);
}
int querymival(int o,int l,int r,int x,int y)
{
if(l>y||r<x)
return 1000000000;
if(l>=x&&r<=y)
return val[o];
pushdown(o,l,r);
int mid=l+r>>1;
int q1=querymival(o<<1,l,mid,x,y);
int q2=querymival(o<<1|1,mid+1,r,x,y);
return min(q1,q2);
}
}tr;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>m;
if(n>100000||m>100000)
exit(111);
p[0]=-1000000000;
for(int i=1;i<=n;i++)
{
cin>>p[i];
if(p[i]<=p[i-1])
exit(112);
}
tr.build(1,1,n);
for(int i=1;i<=m;i++)
{
int opt,x,y;
cin>>opt>>x>>y;
if(opt<1||opt>2)
exit(113);
if(x<1||x>y||y>n)
exit(114);
if(opt==1)
{
tr.update(1,1,n,x,y,p[x-1]);
}
else
{
int ans=1000000000;
int h=tr.querybound(p[x]-1,n);
if(h<y)
{
cout<<p[y]-p[x]<<"\n";
continue;
}
ans=min(ans,tr.querymival(1,1,n,y,h));
if(h<n)
ans=min(ans,p[h+1]-p[x]);
if(ans<=p[n]-p[1])
cout<<ans<<"\n";
else
cout<<-1<<"\n";
}
}
}
}
::::