首页 > 其他分享 >[题解] [HNOI2016] 序列

[题解] [HNOI2016] 序列

时间:2024-08-16 19:15:47浏览次数:8  
标签:rs int 题解 mid leq colon HNOI2016 序列 id

原题链接

题面

给定长度为 $ n $ 的序列:$ a_1, a_2, \cdots , a_n $,记为 \(a[1 \colon n]\)。类似地,\(a[l \colon r]\)( $ 1 \leq l \leq r \leq N$ )是指序列:$ a_{l}, a_{l+1}, \cdots ,a_{r-1}, a_r$。若 \(1\leq l \leq s \leq t \leq r \leq n\),则称 $ a[s \colon t] $ 是 $ a[l \colon r] $ 的子序列。

现在有 $ q $ 个询问,每个询问给定两个数 $ l $ 和 $ r $ ,$ 1 \leq l \leq r \leq n $,求 $ a[l \colon r] $ 的不同子序列的最小值之和。例如,给定序列
$ 5, 2, 4, 1, 3 $,询问给定的两个数为 $ 1 $ 和 $ 3 $,那么 $ a[1 \colon 3] $ 有 $ 6 $ 个子序列 $a[1 \colon 1], a[2 \colon 2], a[3 \colon 3], a[1 \colon 2],a[2 \colon 3], a[1 \colon 3] $,这 $6 $ 个子序列的最小值之和为 \(5+2+4+2+2+2=17\)。

对于 \(100\%\) 的数据, $ 1 \leq n,q \leq 100000$ , $ |a_i| \leq 10^9 $ 。

解析

之前考试遇到过一道叫做“序序”的题,求的是子序列最大值和最小值的积的和。这道题是个丐版,实现起来还是比较简单的。

因为要求所有子序列的和,所以考虑猫树分治。就是把所有询问离线出来,然后挂到树上,找到树上的对应区间段。对于整条询问被这个区间段中点截断的左右两部分递归处理,然后左右合并的部分

蓝色部分对应的是查询区间,黑色的部分是猫树(?线段树),将查询下放到树上,可以发现被第一层区间的中点截成两半。

因为被截断的两个区间的答案和本次查询的答案是不影响的,所以被截断的两区间的答案可以继续下放递归处理。 而合并起来后的区间的答案需要在本层计算出来。考虑双指针。

让 \(i\) 从 \(mid\) 向左移,\(j\) 从 \(mid+1\) 向右移,考虑由 \(i\sim j\) 构成的区间的最小值来源于哪里。固定 \(i\),在右边找到一个分界点 \(d\),表示当 \(j\in[mid+1,d-1]\) 时,最小值在左边区间取得,当 \(j\in [d, r]\) 时,最小值在右边区间取得。当 \(i\) 固定下来后,他的 \(d\) 也确定下来,那么就在 \(mid+1\sim d-1\) 的答案里加上 \(min_l\),在 \(d\sim r\) 的答案里加上 \(min_r\)。这个 \(min_r\) 是右区间每个位置对应的最小值,不是固定的,区间修,区间查,需要用到线段树。因为不同的两种加值对应的系数也不同,所以需要开两棵线段树。并且随着 \(i\) 左移,\(d\) 的位置是单调向右的。所以单次维护复杂度 \(\mathcal{O}(len\log len)\)。总复杂度 \(\mathcal{n\log^2n}\)。

code
#include<bits/stdc++.h>
using namespace std;
#ifdef _WIN32
	#define getchar _getchar_nolock
	#define putchar _putchar_nolock
#else
	#define getchar getchar_unlocked
	#define putchar putchar_unlocked
#endif
template <typename T> inline void rd(T &x){
	x = 0; int f = 1; char ch = getchar();
	while(!isdigit(ch)){ if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x<<1) + (x<<3) + (ch^48), ch = getchar();
	x *= f;
}
template <typename T> inline void wt(T x){
	if(x < 0) putchar('-'), x = -x;
	if(x / 10 > 0) wt(x / 10); putchar(x % 10 + '0');
}
#define int long long
#define ls (id << 1)
#define rs (id << 1 | 1)
constexpr int N = 1e5 + 5;
int n, q, a[N], ans[N], sum[N<<2], jmn[N], s[N];
struct Seg{ int l, r, ai; };
vector<Seg> spl[N<<2]; vector<int> ful[N<<2];
inline void insert(int id, int l, int r, int x, int y, int ai){
    if(l > r) return ;
    if(x <= l && r <= y) return (void)(ful[id].push_back(ai));
    int mid = (l + r) >> 1; 
    if(x <= mid && mid < y) spl[id].push_back(Seg{max(l, x), min(r, y), ai});
    if(x <= mid) insert(ls, l, mid, x, y, ai);
    if(y >  mid) insert(rs, mid+1, r, x, y, ai);
}
namespace ST{
    struct node{ int l, r, sum[2], tag[2], val[2]; }t[N<<2];
    inline void pushup(int id){
        t[id].sum[0] = t[ls].sum[0] + t[rs].sum[0];
        t[id].sum[1] = t[ls].sum[1] + t[rs].sum[1];
    }
    inline void addtag(int id, int k, int p){
        t[id].tag[p] += k; t[id].sum[p] += k * t[id].val[p];
    }
    inline void pushdown(int id){
        if(t[id].tag[0]) addtag(ls, t[id].tag[0], 0), addtag(rs, t[id].tag[0], 0);
        if(t[id].tag[1]) addtag(ls, t[id].tag[1], 1), addtag(rs, t[id].tag[1], 1);
        t[id].tag[0] = t[id].tag[1] = 0;
    }
    inline void build(int id, int l, int r){
        t[id].l = l, t[id].r = r; t[id].val[0] = t[id].val[1] = 0;
        t[id].sum[0] = t[id].sum[1] = t[id].tag[0] = t[id].tag[1] = 0;
        if(l == r) return (void)(t[id].val[1] = jmn[l], t[id].val[0] = 1);
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid+1, r);
        t[id].val[0] = t[ls].val[0] + t[rs].val[0];
        t[id].val[1] = t[ls].val[1] + t[rs].val[1];
        pushup(id);
    }
    inline void modify(int id, int l, int r, int k, int p){
        if(l > r) return;
        if(l <= t[id].l && t[id].r <= r) return addtag(id, k, p);
        pushdown(id); int mid = (t[id].l + t[id].r) >> 1;
        if(l <= mid) modify(ls, l, r, k, p);
        if(r >  mid) modify(rs, l, r, k, p);
        pushup(id);
    }
    inline int query(int id, int l, int r){
        if(l <= t[id].l && t[id].r <= r) return t[id].sum[0] + t[id].sum[1];
        pushdown(id); int mid = t[ls].r, as = 0;
        if(l <= mid) as += query(ls, l, r);
        if(r >  mid) as += query(rs, l, r);
        return as;
    }
}
inline void mao(int id, int l, int r){
    if(l == r){
        sum[id] = a[l];
        for(int it : ful[id]) ans[it] += sum[id];
        return;
    }
    int mid = (l + r) >> 1, imn = a[mid], j = mid + 1, k = 0;
    sort(spl[id].begin(), spl[id].end(), [](const Seg x, const Seg y){ return x.l > y.l; });
    jmn[mid + 1] = a[mid + 1]; s[mid + 1] = a[mid + 1];
    for(int i=mid+2; i<=r; ++i) jmn[i] = min(a[i], jmn[i-1]);
    ST::build(1, mid + 1, r); 
    for(int i=mid; i>=l ;--i){
        imn = min(imn, a[i]); while(j <= r && jmn[j] > imn) ++j;
        ST::modify(1, mid+1, j - 1, imn, 0), ST::modify(1, j, r, 1, 1);
        while(k < (int)spl[id].size() && spl[id][k].l == i) ans[spl[id][k].ai] += ST::query(1, mid+1, spl[id][k].r), ++k;
    }
    sum[id] += ST::query(1, mid+1, r);
    mao(ls, l, mid), mao(rs, mid+1, r);
    sum[id] += sum[ls] + sum[rs];
    for(int it : ful[id]) ans[it] += sum[id];
}
signed main(){
    rd(n), rd(q);
    for(int i=1; i<=n; ++i) rd(a[i]);
    for(int i=1, x, y; i<=q; ++i) rd(x), rd(y), insert(1, 1, n, x, y, i);
    mao(1, 1, n); for(int i=1; i<=q; ++i) wt(ans[i]), putchar('\n');
    return 0;
}

标签:rs,int,题解,mid,leq,colon,HNOI2016,序列,id
From: https://www.cnblogs.com/xiaolemc/p/18363495

相关文章

  • 2024杭电多校第十场 1002树上询问(题解)
    题意给一棵树,每个节点有一个权值,并且权值是一个排列。接下来有多次操作,每次操作要么是交换两个节点权值,要么是询问一个权值区间\([L,R]\),判断是否存在树上的一个路径,使得路径上的权值恰好都在这个区间里分析由于询问的是树上的一个路径,联想到了树上莫队中对路径的处理。这里......
  • 洛谷p1717题解
    题目描述话说发源于小朋友精心设计的游戏被电脑组的童鞋们藐杀之后非常不爽,为了表示安慰和鼓励,VIP999决定请他吃一次“年年大丰收”,为了表示诚意,他还决定亲自去钓鱼。但是,因为还要准备NOIP2013,z老师只给了他 H 个小时的空余时间,假设有 n 个鱼塘都在一条水平路边,从左......
  • 洛谷p2580题解
    题目背景XS中学化学竞赛组教练是一个酷爱炉石的人。他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛CON900)。题目描述这之后校长任命你为特派探员,每天记录他的点名。校长会提供化学竞......
  • 计算1~n的和题解(Mars OJ P1016)
    在这儿问一下,有人用MarsOJ的吗?有的话,评论区里回复一下,谢谢。好了切入正题题目:说明给出正整数n(1<=n<=10⁶),计算1到n的和输入格式一行一个正整数(1<=n<=10⁶)输出格式一行一个整数,表示1到n的和样例 输入数据13输出数据16这题考的时循环,把1~n跑......
  • P4271 [USACO18FEB] New Barns P 题解
    题目传送门前置知识树的直径|最近公共祖先|并查集解法一个显而易见的结论:设点集\(A\)的直径的两个端点为\(u_{1},v_{1}\),另一个点集\(B\)的直径的两个端点为\(u_{2},v_{2}\),则\(A\bigcupB\)的直径端点一定是\(\{u_{1},v_{1},u_{2},v_{2}\}\)中的两个。还有......
  • P8734 奇偶覆盖 题解
    Statement矩形面积并,但是覆盖奇数次、覆盖偶数次的面积要分别输出。Solution提供一种不费脑子的做法:首先离散化、扫描线,问题变成维护区间+1-1、询问全局有多少正数是奇数、多少正数是偶数。若去除“正数”的条件,这是很容易用一个标记下传的线段树维护的,区间分别维护0,1个......
  • Git 命令大全:详细讲解与常见问题解决方案
    目录1.Git基础命令2.分支管理命令3.远程仓库管理命令4.标签管理命令5.其他常用命令6.总结Git是目前最流行的分布式版本控制系统,它使得团队协作和代码管理变得更加高效。本文将详细介绍Git的常用命令及其应用场景,并针对可能遇到的问题提供解决方案。1.Git......
  • Mysql实现自增长编号,日期+序列
    Mysql实现自增长编号,日期+序列,序列定时归零https://blog.csdn.net/u010355502/article/details/47155905/Mysql生成序列---拼接字符串用于业务主键https://blog.csdn.net/Good_omen/article/details/123838440查看所有函数mysqlmysql查看函数命令https://blog.51cto.com/u_16......
  • P8518 [IOI2021] 分糖果 题解
    DescriptionKhong阿姨正在给附近一所学校的学生准备\(n\)盒糖果。盒子的编号分别为\(0\)到\(n-1\),开始时盒子都为空。第\(i\)个盒子\((0\leqi\leqn-1)\)至多可以容纳\(c[i]\)块糖果(容量为\(c[i]\))。Khong阿姨花了\(q\)天时间准备糖果盒。在第\(j\)天......
  • [CEOI2009] photo 题解
    前言题目链接:洛谷。好多错解都被我叉了,所以来贡献一发正确的题解,并予以解释。题意简述平面上有\(n\)个点,现在要求用最少的底边在\(x\)轴上且面积小于等于\(S\)的矩形覆盖所有点,这些矩形可以重叠。问最少矩形数量。矩形顶点不必是整点。\(n\leq100\)。题目分析没什......