1 条题解
-
0
自动搬运
来自洛谷,原作者为

hsfzLZH1
我永远喜欢珂朵莉搬运于
2025-08-24 22:07:24,当前版本为作者最后更新于2019-01-10 20:20:54,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目大意
有一个长度为 的区间,和 次操作。每次操作给出 ,查询 ,并将所有这样的 赋值为 。数据 ** 强制在线 ** 。
如何解密
谁不知道亦或的逆运算是或异啊Subtask#1 20pts
使用二维数组,用 的时间复杂度暴力处理每个查询,更新 数组在范围 之间的值。时间复杂度 。
Subtask#2 30pts
观察到每个查询都是连续的区间,可以用 ** 树套树 ** 来维护这个问题。时间复杂度 。空间复杂度 。
也可以使用 ** 分块套分块 ** 解决,时间复杂度 ,空间复杂度 。
观察到每个人认识的人在这个区间上都是 ** 连续 ** 的。对于一个人,我们只需记录这个人最左认识到的人的编号,和最右认识到的人的编号。注意到“认识”是有双向性的,所以我们可以只存储一个人向左最左认识到的人的编号,或另外一个。以最右认识到的人的编号为例,记为 。初始状态,所有人都不认识,一个人最右认识到的人是他自己,即 。在查询的时候,我们令区间 的人互相认识,就是说对于这个区间上每一个人,如果这个人 之前认识的最右的人在 的左边,那么我们更新 。
时间复杂度 。
Subtask#3 50pts
观察到上一段中所提到的 数组是 ** 严格非降 ** 的。
考虑使用数据结构来维护这个 数组。本题标程所使用的数据结构是 ** 线段树 ** 。
我们需要进行的更新是 ,利用 数组严格非降的性质,我们将一次操作分解为一下几个过程:
-
求 的值;
-
二分求出区间 上使 的最大的 ,记为 ;
-
将区间 赋值为 。
这样,我们就可以在 的时间复杂度下解决这个问题。
代码展示
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1000010; typedef long long ll; ll n,q,lstans,idx,l,r,sum[maxn*4],rv[maxn*4],lzy[maxn*4]; void pushdown(ll o,ll l,ll r) { lzy[o<<1]=lzy[o<<1|1]=lzy[o]; rv[o<<1]=rv[o<<1|1]=lzy[o]; ll mid=(l+r)>>1; sum[o<<1]=(mid-l+1)*lzy[o]; sum[o<<1|1]=(r-(mid+1)+1)*lzy[o]; lzy[o]=0; } void Set(ll o,ll l,ll r,ll sl,ll sr,ll v) { if(sl>r||sr<l)return; if(sl<=l&&r<=sr){lzy[o]=v;sum[o]=(r-l+1)*v;rv[o]=v;return;} ll mid=(l+r)>>1; if(lzy[o])pushdown(o,l,r); Set(o<<1,l,mid,sl,sr,v); Set(o<<1|1,mid+1,r,sl,sr,v); sum[o]=sum[o<<1]+sum[o<<1|1]; rv[o]=rv[o<<1|1]; } ll Query(ll o,ll l,ll r,ll ql,ll qr) { if(ql>r||qr<l)return 0; if(ql<=l&&r<=qr)return sum[o]; ll mid=(l+r)>>1; if(lzy[o])pushdown(o,l,r); return Query(o<<1,l,mid,ql,qr)+Query(o<<1|1,mid+1,r,ql,qr); } ll Find(ll o,ll l,ll r,ll v) { if(l==r)return l; if(lzy[o])pushdown(o,l,r); ll mid=(l+r)>>1; if(rv[o<<1]>=v)return Find(o<<1,l,mid,v); return Find(o<<1|1,mid+1,r,v); } int main() { scanf("%lld%lld",&n,&q); for(ll i=1;i<=n;i++)Set(1,1,n,i,i,i); while(q--) { scanf("%lld%lld",&l,&r); l^=lstans;r^=lstans; idx=Find(1,1,n,r);//the last number index witch <=r idx--; if(idx>=l)lstans=r*(idx-l+1)-Query(1,1,n,l,idx); else lstans=0; printf("%lld\n",lstans); if(idx>=l)Set(1,1,n,l,idx,r); } return 0; }闲言碎语
如果不强制离线有什么好的做法?
出题人本人也想不到 。
为什么要弄Subtask呢?
因为本人的暴力(Subtask#2 的第三种解法)在随机数据下跑得比标程还快!
所以出题人设置了两组极限数据,专门卡时间复杂度为 的暴力,一个卡从正面,一个卡从反面。怎么卡呢?当然是令 偏大,对所有询问按从小到大(或者从大到小)排个序,就可以让这样的暴力跑出 ,出题人是不是很聪明啊 !
如果有人用暴力过了此题,请联系出题人哦! -
- 1
信息
- ID
- 3849
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 上传者