线段树

【日记】12.4

爷,独闯天下 提交于 2019-12-06 13:14:39
12.4日记 CDQ分治 HDU1541:给定一些(a,b),定义(a,b)的等级为满足(a2<=a&&b2<=b)的(a2,b2)的个数。输出等级为0-n-1的星星个数。 二维偏序裸题。第一位排好序,第二维树状数组即可。 注意 :树状数组不可以处理下标为0的情况,因此需要先+1。或者就直接离散化。离散化的时候,注意int len=unique(a+1,a+n+1)-a-1; 线段树 HDU4578:区间加减+区间乘+区间修改+区间询问一次二次三次方和。 我tm哭了,debug了一天。既然是取模,所以lazy[id][3]标记在没有值的时候要设置为-1,不是用0判断。 思路 :修改最优先,乘其次,加减最后。每次pushdown先判断修改,如果没有修改再进行乘和加减。每次修改值的时候,一次性把123次全都修改。 单调数据结构 POJ2559:给N个宽度为1的并列的矩形,求选出一个矩形,面积最大。 思路 :可以贪心,以第i个矩形为最大模板,能选取的最大面积是左边第一个比他小的位置和右边第一个比他小的位置的距离*h[i],先单调栈求出两边第一个比他小的数,最后再扫一遍,都是 \(O(n)\) 的。 但对于这种区间最小值*区间长度的最大值的题目,可以直接单调栈处理。 POJ3494:给一个01矩阵,求最大子矩阵的面积。 思路 :太妙了,扫每一个横行,实际上相当于做了n遍最大矩形并面积。

PKUWC2018题解

女生的网名这么多〃 提交于 2019-12-06 12:59:35
PKUWC2018题解 Minimax 显然最终权值只能是所有叶子中的权值,设 \(f_{i,j}\) 表示以 \(i\) 节点的数字为 \(j\) 的概率,这个dp很简单。 这时候暴力向上合并是 \(O(n^2)\) 的,想办法优化向上合并的效率。 考虑线段树合并,如果只有一个儿子直接继承就行了,关键是两个儿子也就是两颗线段树怎么合并。 对于两颗待合并的线段树,我们设一个函数 \(merge(x,y,sx,sy,v)\) 表示当前合并的根节点分别为 \(x,y\) ,其中 \(sx\) 表示 \(y\) 对 \(x\) 贡献为 \(sx\) , \(sy\) 表示 \(x\) 对 \(y\) 贡献为 \(sy\) , \(v\) 表示我们在原树上根节点所对应的概率。 那么当 \(x\) 或 \(y\) 为空时,可以直接对非空的根打上一个所对应贡献的标记。 因为事实上这棵值域线段树中是对应了一段区间的,而往下递归时每棵树的权值大小关系确定我们将一段区间对于另外一段区间的贡献直接作用在 \(sx,sy\) 上,向下递归即可。 实现细节详见代码。 代码 Slay the Spire 令强化牌为 \(\{a\}\) ,攻击牌为 \(\{b\}\) ,将 \(a,b\) 按照从大到小排序。 设 \(f_{i,j}\) 表示强化牌选到且选第 \(i\) 张,选了 \(j\) 张牌方案数的

[POI2015]PUS [线段树优化建图]

不打扰是莪最后的温柔 提交于 2019-12-06 12:58:46
problem 线段树优化建图,拓扑,没了。 #include <bits/stdc++.h> #define ls(x) ch[x][0] #define rs(x) ch[x][1] #define rep(i , j , k) for(int i = j ; i <= k ; i ++) #define Rep(i , j , k) for(int i = j ; i >= k ; i --) using namespace std ; using ll = long long ; using pii = pair <int , int> ; using vii = vector <int> ; // #define int long long auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ; auto _ios = [&]() { ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ; } ; namespace stO_ATS_Orz { template < class T > void cmax(T & x , T y) { if(x < y) x = y ; }

[SNOI2017]炸弹[线段树优化建图]

ぐ巨炮叔叔 提交于 2019-12-06 12:58:35
[SNOI2017]炸弹 线段树优化建图,然后跑一边tarjan把点全部缩起来,炸一次肯定是有连锁反应的所以整个连通块都一样…于是就可以发现有些是只有单向边的不能忘记更新,没了。 #include <bits/stdc++.h> #define ls(x) ch[x][0] #define rs(x) ch[x][1] #define rep(i , j , k) for(int i = j ; i <= k ; i ++) #define Rep(i , j , k) for(int i = j ; i >= k ; i --) using namespace std ; using ll = long long ; using pii = pair <int , int> ; using vii = vector <int> ; #define int long long auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ; auto _ios = [&]() { ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ; } ; namespace stO_ATS_Orz { template

poj 2991 起重机

落爺英雄遲暮 提交于 2019-12-06 12:49:56
地址 http://poj.org/problem?id=2991 题解 本来以为这是一个简单的线段树模板 不料始终不太明白线段树如何记录转动角度后的各个线段端的XY值 学习了网络上的一些博客题解 感觉似是而非 谈到复数 角度 向量等,有点不太好理解 现在这里将自己的理解记录如下 如图 1 预备知识 使用线段树记录的内容如下 指示某段线段的组合 以第一条线段为垂直 最后的线段的端点的X Y值 途中1~2 线段 和3~5线段 就是线段树节点1~5的子节点 那么线段树节点1~5 就记录1~5结合后的X Y 值以及两个子节点结合的角度值 由于3~5线段的XY 是以自己的第一条线段为垂直起点为0 0 计算出来的X Y 那么在于1~2线段合并的时候 并不是简单的将两子节点的X Y相加即可得到1~5线段的XY 而是要加入旋转了相对角度 该角度由记录1~5线段的线段树节点记录 1~2线段部分的X Y值 旋转相对角度的公式推导如下 https://blog.csdn.net/hjq376247328/article/details/45113563 其实也就是 xNew = x * cosB - y * sinB yNew = x * sinB + y * cosB 再来和 1~2线段的X Y相加即可得到1~5线段的X Y,并将该两子节点的相对角度记录在父节点中 预备知识讲完 2 解答步骤如下 一

CF786B Legacy[线段树优化建图]

做~自己de王妃 提交于 2019-12-06 12:47:42
Problem 线段树建图 大概就是这样子… 众所周知 线段树的一个编号 \(p\) 指的是 \([L,R]\) 这段区间 初始化 \(p\) 向 \(p\) 的左儿子 和 \(p\) 的右儿子连边… 那么就完成了 \(p\) 向 \([L,R]\) 连边 即如果对 \(p\) 连边 就相当于对 \([L,R]\) 连边… ( 大概解释的够清楚了? ) 所以这题可以建两颗线段树 一棵管入边 一棵管出边… 最后跑个最短路就出来了… #include <bits/stdc++.h> #define ls(x) ch[x][0] #define rs(x) ch[x][1] #define rep(i , j , k) for(int i = j ; i <= k ; i ++) #define Rep(i , j , k) for(int i = j ; i >= k ; i --) using namespace std ; using ll = long long ; using pii = pair <int , int> ; using vii = vector <int> ; #define int long long auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ;

CodeForces - 1263E(线段树维护前缀和最值)

核能气质少年 提交于 2019-12-06 11:05:40
题意 https://vjudge.net/problem/CodeForces-1263E 您要设计一个只有一行的打字机,这一行的长度是无限大,一开始可以认为每个字符都是空。您的打字机有一个光标只指向一个字符,一开始指向最左侧的字符。 使用者有三种操作: L 将光标向左移一格(当光标已经在最左侧时,忽略这次操作) R 将光标向右移一格 一个小写字符或者'(',')' 将当前字符替换为给定字符 您需要在每次操作后,判断这一行是否是合法括号序列(例如 (ahakioi) 就是合法的, (ige))(tscore 就是非法的),不是输出 -1 ,否则输出最多嵌套数(例如 ()(())()() 的最多嵌套数是 2, (()(()())())(()) 的最多嵌套数是 3)。 思路 看上去是个大模拟,实则是个线段树的技巧题。 用一个pos记录光标的位置。 记'('为1,')'为-1,其他字符为0,这样如果有某个前缀和<0(只用判断前缀和最小值<0即可),那么就不合法,或者所有加起来不等于0,也不合法。 单点更新,维护区间和sum,前缀和最小值mn,前缀和最大值mx。区间前缀和最小值和左右子树有关,比较左子树的前缀和最小值、右子树的前缀和最小值+左子树的区间和,用小的那个更新mn[rt],前缀和最大值同理。 那么在合法的情况下,前缀和的最大值即为最多嵌套数。 代码 #include<bits

[题解][Codeforces]Codeforces Round #602 (Div. 1) 简要题解

主宰稳场 提交于 2019-12-06 10:36:42
orz d jq_cpp lgm A 题意 给定一个分别含有 \(\frac n2\) 个左括号和右括号的括号序列 每次可以将序列的一个区间翻转 求一个不超过 \(n\) 次的操作方案,使得操作完之后的序列是合法括号序列,并且恰好有 \(k\) 个前缀是合法括号序列 多组数据,所有数据的 \(n\) 之和不超过 \(2000\) 做法:构造 随便生成一个合法的目标序列,如 \(k-1\) 个 () 相接再接上 \(\frac n2-k+1\) 个 ( 和 \(\frac n2-k+1\) 个 ) 依次从左往右检查,对于位置 \(i\) 如果和目标序列不一样则在右边找一个 \(j\) 使得当前序列第 \(j\) 个元素和目标序列第 \(i\) 个元素相等,并翻转区间 \([i,j]\) \(O(n^2)\) 代码 #include <bits/stdc++.h> template <class T> inline void read(T &res) { res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9'

线段树合并[学习笔记]

♀尐吖头ヾ 提交于 2019-12-06 10:25:33
前置知识: 动态开点线段树/主席树 线段树合并,跟名字一样,就是合并两颗线段树的信息… 合并两颗线段树的方法是 递归下去一直合并两个如果俩节点都有…就新建一个维护当前两个的信息然后取而代之 如果只有一个有 那就返回那一个就够了… 这个blog内借鉴了一些洛谷的题解…因为作者实在菜的可怜 ( 懒 ) 先放个 板子题 吧 求子树几个比他权值大的 inline int Merge(int u , int v) { if(!u || ! v) return u | v ; int t = ++ cnt ; sum[t] = sum[u] + sum[v] ; ls[t] = Merge(ls[u] , ls[v]) ; rs[t] = Merge(rs[u] , rs[v]) ; return t ; } 就像是这样… 然后每次递归到最底层 下属信息合并…然后查询 最后输出,没了。 // luogu-judger-enable-o2 //Isaunoya #include<bits/stdc++.h> using namespace std ; inline int read() { register int x = 0 ; register int f = 1 ; register char c = getchar() ; for( ; ! isdigit(c) ; c =

线段树模板

蹲街弑〆低调 提交于 2019-12-06 08:33:01
单点修改,区间查询和 #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <vector> const int maxn = 1e5+5; const int inf = 0x3f3f3f3f; const double eps = 1e-5; using namespace std; int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return f*x; } struct node{ int l,r; int data; #define lson(p) p * 2 #define rson(p) p * 2 + 1 }tree[maxn<<2]; void pushup(int p){ tree[p].data = tree[lson(p)].data + tree[rson(p)].data; } void bulid(int p,int l,int r){ tree[p].l = l,tree[p].r = r; if(r