bzoj 5286 [Hnoi2018]转盘

http://www.elijahqi.win/archives/3634
题目描述
一次小G和小H准备去聚餐,但是由于太麻烦了于是题面简化如下:

一个转盘上有摆成一圈的

n

n 个物品(编号1~

n

n ),其中的

i

i 个物品会在

T_i

Ti​ 时刻出现。

在0时刻时,小G可以任选

n

n 个物品中的一个,我们将其编号为

s_0

s0​ 。并且如果

i

i 时刻选择了物品

s_i

si​ ,那么

i+1

i+1时刻可以继续选择当前物品或选择下一个物品。当

s_i

si​ 为

n

n 时,下一个物品为物品

1

1 ,否则为物品

s_{i}+1

si​+1 。在每一时刻(包括0时刻),如果小G选择的物品已经出现了,那么小G将会标记它。小H想知道,在物品选择的最优策略下,小G什么时候能标记所有物品?

但麻烦的是,物品的出现时间会不时修改。我们将其描述为

m

m 次修改,每次修改将改变其中一个物品的出现时间。每次修改后,你也需求出当前局面的答案。对于其中部分测试点,小H还追加了强制在线的要求。

输入输出格式
输入格式:

第一行三个非负整数

n

n 、

m

m 、

p

p ,代表一共有

n

n 个物品,

m

m 次修改。

p

p 只有0或1两种取值,强制在线时

p

p 为1,否则

p

p 为0.

接下来一行,有

n

n 个非负整数,第

i

i 个数

T_i

Ti​ 代表物品

i

i 的出现时间。

接下来

m

m 行,每行两个非负整数

x

x 、

y

y ,代表一次修改及询问。修改方式如下:

(1)如果

p=0

p=0 ,则表示物品

x

x 的出现时间

T_x

Tx​ 修改为

y

y 。

(2)如果

p=1

p=1 ,在先将

x

x 和

y

y 分别异或

LastAns

LastAns ,得到

x’

x′ 和

y’

y′ ,然后将物品

x’

x′ 的出现时间

T_{x’}

Tx′​ 修改为

y’

y′ 。其中的

LastAns

LastAns 是前一个询问的结果;特别的,第一次修改时

LastAns

LastAns 为初始局面的答案。

保证输入合法。

输出格式:

第一行一个整数,代表初始局面的答案。

接下来

m

m 行每行一个整数,分别代表每次修改后的答案。

输入输出样例
输入样例#1: 复制

5 3 0
1 2 3 4 5
3 5
5 0
1 4
输出样例#1: 复制

5
7
6
7
非常有趣的一题 很喜欢..

首先应该都能想到 一定不会经过某个点两次

那么考虑不妨我们让等待时间都在第一个点等待即可

这样一定不会差 因为考虑如果在中间一个点等待的话相当于他前面的点都提早到达并不会使得答案变得更优

那么相当于我们要求的是针对每个起点的出发的 (长度区间为n的这么一段区域所有的出现时间减去出发时间的最大值 ) 这么一个东西取min 最后再加上整个区间走完的代价即可

考虑线段树维护

那么设a[i]表示T[i]-i

考虑设mx[x]表示线段树上这个区间 a的最大值

ans[x]表示这个区间 左半边为起点 完全包含右区间所有a值中最大的最小值

即最后的ans[1]即是答案 考虑在线段树维护答案的时候 可能区间长度会大于我们需要的规定长度的n的那个区间 但是并不妨碍 因为超过时候我们发现超过区间的值其实是包含了 如 T[l],T[l+n]这样的东西 但是因为T[l+n]的值比T[l]的值小 那么可以知道这并不会对我们最后的答案产生影响

最后祭出 学习了哪个大佬的blog orzz

https://blog.csdn.net/litble/article/details/80187830

#include<cstdio>
#include<cctype>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=2e5+20;
int mx[N<<2],ans[N<<2],n,m,p,T[N],a[N],last_ans;
inline int merge(int x,int l,int r,int v){
    if (l==r) return l+max(mx[x],v);int mid=l+r>>1;
    if (mx[rc]>=v) return min(ans[x],merge(rc,mid+1,r,v));
    else return min(merge(lc,l,mid,v),mid+1+v);
}
inline void update(int x,int l,int r){
    mx[x]=max(mx[lc],mx[rc]);
    ans[x]=merge(lc,l,l+r>>1,mx[rc]);
}
inline void build(int x,int l,int r){
    if (l==r){mx[x]=a[l];return;}int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);update(x,l,r);    
}
inline void modify(int x,int l,int r,int p){
    if (l==r) {mx[x]=a[l];return;}int mid=l+r>>1;
    if(p<=mid) modify(lc,l,mid,p);else modify(rc,mid+1,r,p);
    update(x,l,r);
}
int main(){
    freopen("bzoj5286.in","r",stdin);
    n=read();m=read();p=read();
    for (int i=1;i<=n;++i) T[i]=read(),T[i+n]=T[i],a[i]=T[i]-i,a[i+n]=T[i]-i-n;
    build(1,1,n<<1);last_ans=ans[1]+n-1;printf("%d\n",last_ans);
    for (int i=1;i<=m;++i){
        int x=read(),y=read();if (p) x^=last_ans,y^=last_ans;
        T[x]=y;T[x+n]=y;a[x]=T[x]-x;a[x+n]=T[x]-x-n;
        modify(1,1,n<<1,x);modify(1,1,n<<1,x+n);
        last_ans=ans[1]+n-1;printf("%d\n",last_ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值