线段树

【权值线段树】

徘徊边缘 提交于 2019-11-29 13:24:55
一道简单模板题; 也记记吧,免得脑子不灵光又忘记了。 洛谷 1168 #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; int n; int a[N],b[N]; struct Tree { int l,r,mid; int num; }tree[N<<2]; int read() { char c=getchar();int num=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num; } void build(int root,int l,int r) { tree[root].l=l,tree[root].r=r,tree[root].mid=l+r>>1; if(l==r) return; build(root<<1,l,tree[root].mid); build(root<<1|1,tree[root].mid+1,r); } void update(int root,int x) { ++tree[root].num; if

干翻线段树——指令集优化指北

余生长醉 提交于 2019-11-29 13:18:55
前言 在我们刷题的时候,总会碰到一些关于 区间操作/修改 的题目。这些题目往往要求我们维护一段区间,支持对一段区间进行查询/修改操作。这些题目有如 树状数组1 一般的 简单题 ,也有如[无聊的数列][2]一般,线段树、树状数组能够完成,但是码量长,可读性差,思考难度大的 较难题 。这种题目对时间的要求非常严格, 询问/查询次数 与 序列长度 均在 \(10^5\) 级别或以上,能够使 \(O(n^2)\) 的暴力算法望屋窃叹。那么,有没有什么方法,可以通过数据,代码又易于实现呢? 指令集优化 今天我要讲的 指令集优化 ,就是对这个问题的最佳解答。那么,什么是指令集?指令集优化又能干什么? 什么是指令集? 指令集是存储在CPU内部,对CPU运算进行指导和优化的指令集合。 简单来说,我们写的程序,无论是C, C++, Java, Python,还是其它高级语言,CPU都是看不懂的。这个时候,我们的编译器(解释器)把这些语言翻译为汇编代码,进而翻译01编码,即CPU能看得懂的 指令集命令 。 但是,由于高级语言的特性,在向下翻译的过程中,为了保证其 正确性 与 兼容性 ,编译器会产生大量冗余代码。这些代码的存在使程序运行的速度变得慢了许多。 那我能不能把那些冗余代码删掉啊? 通常情况下,这些冗余代码是不能完全弄掉的。比较特殊的方法是开启编译器优化,也就是我们常说的 吸氧(-o2),吸臭氧

权值线段树 洛谷1168

牧云@^-^@ 提交于 2019-11-29 12:19:28
这道题好像不止权值线段树一个做法; 不过本人是用权值线段树来解的,由于数据开到了1e9,所以得离散化。 离散化之后,先build,记录每一个节点的所包含的数字大小范围, 然后在慢慢建树,建树的过程中,当单数的时候,就进行查询; 1 #include<cstdio> 2 #include<algorithm> 3 #include<math.h> 4 #include<string.h> 5 #include<queue> 6 using namespace std; 7 const int maxn=1e5+10; 8 int a[maxn],b[maxn]; 9 struct node 10 { 11 int l,r,mid; 12 int num; 13 }tree[maxn<<2]; 14 void build(int l,int r,int cur) 15 { 16 tree[cur].l=l,tree[cur].r=r; 17 tree[cur].num=0; 18 tree[cur].mid=(l+r)/2; 19 if(l==r) return; 20 build(l,tree[cur].mid,cur<<1); 21 build(tree[cur].mid+1,r,cur<<1|1); 22 } 23 void update(int pos,int cur) 24

bzoj2733 永无乡 (并查集+线段树合并)

浪尽此生 提交于 2019-11-29 12:17:10
问题描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。 Input 输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000 对于 100%的数据 n≤100000,m≤n,q≤300000 Output 对于每个

动态开点线段树

这一生的挚爱 提交于 2019-11-29 11:15:01
练习树剖的时候找到了 https://www.luogu.org/problem/P3313 于是顺便学习了一下动态开点线段树。 这里写一点心得。 很多线段树不是满二叉树就会造成空间浪费,而动态开点就可以解决这个问题。那么怎么动态开点呢? 结合这题来看一下吧。 来源: https://www.cnblogs.com/Kylin-xy/p/11516097.html

CF1217E Sum Queries? (线段树)

爷,独闯天下 提交于 2019-11-29 10:30:28
完了,前几天才说 edu 的 DEF 都不会,现在打脸了吧 qwq 其实在刚说完这句话 1min 就会了 D,3min 就会了 E 发现,对于大小 \(\ge 3\) 的不平衡集合,它至少有一个大小为 \(2\) 的子集是不平衡的。 证明,发现对于大小为 \(2\) 的集合,平衡当且仅当两数的数位交为空(对于任意一位,至多一个数在这一位上不是 \(0\) )。 反证一波,如果大集合没有大小为 \(2\) 的不平衡集合,那么任意两数的数位交都为空,那么大集合也是平衡的,矛盾了。 所以,只需要考虑大小为 \(2\) 的集合。 这个就能线段树简单做了,每个线段树维护 \(ans\) 表示这个区间中的答案, \(mn_i\) 表示这个区间中第 \(i\) 位有值的数的最小值。pushup 具体看代码。 时间复杂度 \(O((n+q\log n)\log a_i)\) 。 #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> PII; const int maxn=200020,pw[9]={1,10,100,1000,10000,100000,1000000,10000000,100000000}; #define MP make_pair #define PB

对权值线段树剪枝的误解--以HDU6703为例

匆匆过客 提交于 2019-11-29 10:13:29
引子 对hdu6703,首先将问题转化为“询问一个排列中大于等于k的值里,下标超过r的最小权值是多少” 我们采用官方题解中的做法:权值线段树+剪枝 对(a[i],i)建线段树,查询权值线段树的[k,n]中第一个下标超过r的值 代码是这样的 int ask(int l, int r, int root){ int mid = (l+r)>>1; if(mx[root]<=R)return -1; if(l==r){return l;} int ans = -1; if(k<=mid)ans=ask(lson); if(ans==-1)ans=ask(rson); return ans; } 这把辣鸡的我给看meng了: 在R=n的时候,最坏情况下一直向左递归,并且没找到然后向右递归,再向右递归的同时又重复没找到,这个ask不就退化成O(n)的了吗? 思考 经过半个月的思考 (被虐) ,我大概懂了这个剪枝 首先分析以下几个问题: 什么情况下才会递归下去? 由代码第三行的 if(mx[root]<=R)return -1; 我们可以知道,只有当前权值区间 \((l,r)\) 的最大下标超过R才 可能 存在答案 什么情况下会往左递归并且不会从左边的递归返回答案? 假设当前权值区间为 \([l,r]\) 如果往左边递归没有O(1)返回的话,根据上面的结论,那么一定是因为左区间 \([l

bzoj5312 冒险(吉司机线段树)题解

折月煮酒 提交于 2019-11-29 10:10:56
题意: 已知 \(n\) 个数字,进行以下操作: \(1.\) 区间 \([L,R]\) 按位与 \(x\) \(2.\) 区间 \([L,R]\) 按位或 \(x\) \(3.\) 区间 \([L,R]\) 询问最大值 思路: 吉司机线段树。 我们按位考虑,维护区间或 \(\_or\) 和区间与 \(\_and\) ,那么得到区间非公有的 \(1\) 为 \((\_or \oplus \_and)\) ,那么如果对所有的非公有的 \(1\) 影响都一样就不会对最大值有影响,那么就直接打标机,否则继续往下更新。即 \[ [(\_or[rt] \oplus \_and[rt]) \& x] == 0 || [(\_or[rt] \oplus \_and[rt]) \& x] == (\_or[rt] \oplus \_and[rt]) \] 时就直接打标机。 代码: #include<map> #include<set> #include<queue> #include<cmath> #include<stack> #include<ctime> #include<vector> #include<cstdio> #include<string> #include<cstring> #include<sstream> #include<iostream> #include

线段树推式子版

亡梦爱人 提交于 2019-11-29 09:44:36
题:https://www.luogu.org/problem/P2221#submit 求:a n s = i = l ∑ r ​ a [ i ] ∗ ( r − i + 1 ) ( i − l + 1 ) #include<bits/stdc++.h> using namespace std; typedef long long ll; #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r const int M=1e5+5; struct node{ ll sum[6]; }tree[M<<2]; ll lazy[M<<2]; ll sum1,sum2,sum3; void build(int root,ll l,ll r){ if(l==r){ tree[root].sum[4]=l; tree[root].sum[5]=1ll*l*l; return ; } int midd=(l+r)>>1; build(lson); build(rson); tree[root].sum[4]=tree[root<<1|1].sum[4]+tree[root<<1].sum[4]; tree[root].sum[5]=tree[root<<1|1].sum[5]+tree[root<<1].sum[5]; } void

线段树双tag+差分数组——cf1208E

拥有回忆 提交于 2019-11-29 09:40:42
写了一上午 /* 对于每个数组a[],先排序然后从大到小把a[i]放进线段树更新 设a[i]的位置是pos,那么其可更新的区间是[pos,w-(li-pos)] 线段树结点保存 tag=now表示当前区间已经被更新满了,无需再往下更新 flag=now表示当前区间被更新过 flag=now-1表示当前区间从未被更新过,可以直接进行覆盖 */ #include<bits/stdc++.h> #include<vector> using namespace std; #define N 1000005 #define ll long long ll ans[N]; ll n,w; struct Node{ll pos,a;}; int cmp(Node a,Node b){return a.a>b.a;} vector<Node>v[N]; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 int tag[N<<2],flag[N<<2],now; void pushup(int rt){ if(flag[rt<<1]==now || flag[rt<<1|1]==now) flag[rt]=now; if(tag[rt<<1]==now && tag[rt<<1|1]==now) tag[rt]=now; } void update