【UOJ#169】元旦老人与数列

论文题。

考虑到这题的维护和区间操作是反向的,也就是说无法像V那题快速的合并标记。

我们知道,一个区间的最小值和其他值是可以分开来维护的,因为如果一个区间被整体覆盖,那么最小值始终是最小值。

对于被覆盖一半的区间,讨论一下即可。

对于每个最小值和次小值记录前缀最小值,当前/历史最小值,当到达合法区间的时候:

如果区间最小值>x,直接退出。

如果minv[o]<x<semn[o],那么更新当前的次小值

反之继续向下。

#include<bits/stdc++.h>
const int inf=2e9+7;
const int N=500005;
using namespace std;
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘);
    do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘);
    return f*x;
}
int n,m;
struct Segment_Tree_Beats{
#define lson (o<<1)
#define rson (o<<1|1)
    int his[N<<2],pra[N<<2],prb[N<<2],a[N<<2],b[N<<2],minv[N<<2],semn[N<<2];
    inline void pushup(int o){
        if(minv[lson]==minv[rson])minv[o]=minv[lson],semn[o]=min(semn[lson],semn[rson]);
        else if(minv[lson]<minv[rson]){minv[o]=minv[lson];semn[o]=min(semn[lson],minv[rson]);}
        else{minv[o]=minv[rson];semn[o]=min(semn[rson],minv[lson]);}
        his[o]=min(his[lson],his[rson]);
    }
    inline void puttag(int o,int v){
        his[o]=min(his[o],minv[o]+v);
        a[o]+=v;b[o]+=v;minv[o]+=v;semn[o]+=v;semn[o]=min(semn[o],inf);
        pra[o]=min(pra[o],a[o]);
        prb[o]=min(prb[o],b[o]);
    }
    inline void plus(int o,int ax,int pa,int bx,int pb){
        his[o]=min(his[o],minv[o]+pa);
        pra[o]=min(pra[o],pa+a[o]);
        prb[o]=min(prb[o],pb+b[o]);
        a[o]+=ax;b[o]+=bx;minv[o]+=ax;semn[o]+=bx;semn[o]=min(semn[o],inf);
    }
    inline void pushdown(int o){
        int mmmm=min(minv[lson],minv[rson]);
        if(minv[lson]==mmmm)plus(lson,a[o],pra[o],b[o],prb[o]);else plus(lson,b[o],prb[o],b[o],prb[o]);
        if(minv[rson]==mmmm)plus(rson,a[o],pra[o],b[o],prb[o]);else plus(rson,b[o],prb[o],b[o],prb[o]);
        a[o]=b[o]=pra[o]=prb[o]=0;
    }
    inline void build(int o,int l,int r){
        if(l==r){
            minv[o]=read();his[o]=minv[o];semn[o]=inf;
            return;
        }
        int mid=(l+r)>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(o);
    }
    inline void optadd(int o,int l,int r,int ql,int qr,int v){
        if(ql<=l&&r<=qr){puttag(o,v);return;}
        int mid=(l+r)>>1;pushdown(o);
        if(ql<=mid)optadd(lson,l,mid,ql,qr,v);
        if(qr>mid)optadd(rson,mid+1,r,ql,qr,v);
        pushup(o);
    }
    inline void dfs(int o,int l,int r,int v){
        if(minv[o]>=v)return;
        if(semn[o]>v){
            v-=minv[o];his[o]=min(his[o],minv[o]+v);
            pra[o]=min(pra[o],a[o]+v);
            a[o]+=v;minv[o]+=v;return;
        }
        int mid=(l+r)>>1;pushdown(o);
        dfs(lson,l,mid,v);dfs(rson,mid+1,r,v);
        pushup(o);
    }
    inline void change(int o,int l,int r,int ql,int qr,int v){
        if(ql<=l&&r<=qr){dfs(o,l,r,v);return;}
        int mid=(l+r)>>1;pushdown(o);
        if(ql<=mid)change(lson,l,mid,ql,qr,v);
        if(qr>mid)change(rson,mid+1,r,ql,qr,v);
        pushup(o);
    }
    inline int querynow(int o,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return minv[o];
        int mid=(l+r)>>1,ans=inf;pushdown(o);
        if(ql<=mid)ans=min(ans,querynow(lson,l,mid,ql,qr));
        if(qr>mid)ans=min(ans,querynow(rson,mid+1,r,ql,qr));
        return ans;
    }
    inline int queryhis(int o,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return his[o];
        int mid=(l+r)>>1,ans=inf;pushdown(o);
        if(ql<=mid)ans=min(ans,queryhis(lson,l,mid,ql,qr));
        if(qr>mid)ans=min(ans,queryhis(rson,mid+1,r,ql,qr));
        return ans;
    }
}T;
int main(){
    n=read();m=read();
    T.build(1,1,n);
    while(m--){
        int opt=read(),l=read(),r=read();
        if(opt==1){int x=read();T.optadd(1,1,n,l,r,x);}
        if(opt==2){int x=read();T.change(1,1,n,l,r,x);}
        if(opt==3)printf("%d\n",T.querynow(1,1,n,l,r));
        if(opt==4)printf("%d\n",T.queryhis(1,1,n,l,r));
    }
}
时间: 2024-10-18 16:55:37

【UOJ#169】元旦老人与数列的相关文章

UOJ169. 【UR #11】元旦老人与数列

传送门 考虑用 \(segment~tree~beats\) 那一套理论,维护区间最小值 \(mn\) 和严格次小值 \(se\) 那么可以直接 \(mlog^2n\) 维护前三个操作 考虑维护历史最小值,先维护历史最小标记 写了写发现 \(max\) 那个修改不好操作 对于 \(max\) 操作来说,只会在 \(mn< v <se\) 的时候打上标记 这就相当于区间内等于 \(mn\) 的权值都要变成 \(v\) 那么 \(max\) 操作就可以变成对区间最小值的加法操作 而 \(v<

UOJ 52 元旦激光炮

http://uoj.ac/problem/52 题意:每次可以得到3个序列中 思路:每次分别取出三个序列的K/3长度的位置,取最小的那个,然后每次减掉它,总复杂度是Nlog3N #include "kth.h" #include<algorithm> int C[2000005]; int query_kth(int n_a, int n_b, int n_c, int k) { int Len=k; int la=0,lb=0,lc=0; int ans=0; whil

uoj167 元旦老人与汉诺塔(记忆化搜索)

QwQ太懒了,题目直接复制uoj的了 QwQ这个题可以说是十分玄学的一道题了 首先可以暴搜,就是\(dfs\)然后模拟每个过程是哪个柱子向哪个柱子移动 不多解释了,不过实现起来还是有一点点难度的 直接上代码吧 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using na

关于吉利线段树

引言 这玩意儿又称\(Segment\ Tree\ Beats\) .由吉老师.\(Picks\).美国队长三位知名毒瘤发明. 我的精神受到了污染...... 线段树简介 线段树什么的太难了,不是很会啊..... 线段树最强的地方在于,如果标记支持合并,那么我们就能够快速进行区间修改了. 一类特殊标记 维护这样一个标记\((a,b)\) ,表示\(x\to max(x + a , b)\) . 显然标记可合并:\((a,b) + (c,d) = (a + c , max(b + c , d))\

[UOJ Round#4 A] [#51] 元旦三侠的游戏

题目链接:UOJ - 51 据说这题与 CF 39E 类似. 题目分析 一看题目描述,啊,博弈论,不会!等待爆零吧... 这时,XCJ神犇拯救了我,他说,这题可以直接搜啊. 注意!是用记忆化搜索,状态为 (a, b) . 是这样的:我们从后面倒着推,对于一个无法再增加 a 或 b 的 (a, b) 状态,当前走的人必败.这是终止的状态. 而对于一个不是终止状态的状态 (a, b) ,可能有两种后继状态 (a + 1, b) || (a, b + 1) ,我们递归先求出这两个后继状态是必败还是必胜

【uoj#51】[UR #4]元旦三侠的游戏 博弈论+dp

题目描述 给出 $n$ 和 $m$ ,$m$ 次询问.每次询问给出 $a$ 和 $b$ ,两人轮流选择:将 $a$ 加一或者将 $b$ 加一,但必须保证 $a^b\le n$ ,无法操作者输,问先手是否必胜. $n\le 10^9$ ,$m\le 10^5$ ,$a\ge 2$ ,$b\ge 1$ ,$a^b\le n$ 题解 博弈论+dp 显然可以想到预处理 $f[i][j]$ 表示 $a$ 为 $i$ ,$b$ 为 $j$ 时先手能否胜利.显然由 $f[i+1][j]$ 和 $f[i][j+

UOJ#52. 【UR #4】元旦激光炮(交互)

题意 给出三个已经排好序的数组$a, b, c$ 在$100$次询问内找出第$k$小的元素 Sol 一种很显然的$log^2n$的做法:首先在$a$中二分,然后再$b,c$中二分.这样可以得到$60$分的好成绩. 然而这算法就没什么优化的空间了... 考虑另一种做法. 我们每次对三个数组询问第$\frac{3}{k}$个数. 然后我们可以直接把最小对应的那一段抛弃.正确性显然吧.或者你可以考虑一下最坏情况 那么$k$就缩小了$\frac{1}{3}$ 算一下,查询次数不会超过$99$. 具体可以

BZOJ 1500 维修数列

Description Input 输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的表格. Output 对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行. Sample Input 9 8 2 -6 3 5 1 -5 -3 6 3 GET-SUM 5 4 MAX-SUM INSERT 8 3 -5 7 2 DELETE 12

BZOJ1500 维修数列

1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MB Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的表格.任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内.插入的数字总数不超过4 000 000个,输入文