单点修改和区间求和的模板
luogu P3374
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#define MOD 1000000007
using namespace std;
typedef long long ll;
int n, m;
int a[1000000];
int v[4000000];
void build(int x, int l, int r){
if(l == r){
v[x] = a[l];
return;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid + 1, r);
v[x] = v[x<<1] + v[x<<1|1];
}
//y is the node you want to modify
void modify(int x, int l, int r, int y, int z){
if(l == r){
v[x] += z;
return ;
}
int mid = (l + r)>>1;
if(y <= mid){
modify(x<<1, l, mid, y, z);
}
else{
modify(x<<1|1, mid+1, r, y, z);
}
v[x] = v[x<<1]+v[x<<1|1];
}
ll query(int x, int l, int r, int ql, int qr){
if(l == ql && r == qr){
return v[x];
}
int mid = (l + r)>>1;
if(ql <= mid && qr>mid){
return query(x<<1, l, mid, ql, mid) +
query(x<<1|1, mid + 1, r, mid + 1, qr);
}
else if(qr <= mid){
return query(x<<1, l, mid, ql, qr);
}
else{
return query(x<<1|1, mid + 1, r, ql, qr);
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
build(1, 1, n);
int l, r, k;
while(m--){
cin >> k >> l >> r;
if(k == 1){
modify(1, 1, n, l, r);
}else{
cout << query(1, 1, n, l, r) << endl;
}
}
return 0;
}
也可以用来求LCA.
leetcode 236
class Solution {
public:
map<int, TreeNode*> a;
int atop = 0;
map<TreeNode*, int> b;
map<TreeNode*, int> dp;
TreeNode* v[610000];
void dfs(TreeNode* root){
if(!root)return;
a[++atop] = root;
b[root] = atop;
if(root->left){
dp[root->left] = dp[root] + 1;
dfs(root->left);
a[++atop] = root;
}
if(root->right){
dp[root->right] = dp[root] + 1;
dfs(root->right);
a[++atop] = root;
}
}
TreeNode* cmp(TreeNode* x, TreeNode* y){
if(dp[x] < dp[y])return x;
return y;
}
void build(int x, int l, int r){
if(l == r){
v[x] = a[l];
return;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid + 1, r);
v[x] = cmp(v[x<<1], v[x<<1|1]);
}
TreeNode* query(int x, int l, int r, int ql, int qr){
if(l == ql && r==qr){
return v[x];
}
int mid = (l + r) >> 1;
if(ql <= mid && mid < qr){
return cmp(query(x<<1, l, mid, ql, mid),
query(x<<1|1, mid+1, r, mid + 1, qr));
}
else if(qr <= mid){
return query(x<<1,l,mid, ql, qr);
}
else{
return query(x<<1|1, mid+1, r, ql, qr);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root);
build(1, 1, atop);
if(b[p] > b[q]){
return query(1, 1, atop, b[q], b[p]);
}else{
return query(1, 1, atop, b[p], b[q]);
}
}
};
luogu P3379 求LCA
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#define MOD 1000000007
using namespace std;
typedef long long ll;
int n, m, o;
struct edge{
int next, y;
}e[1100000];
int link[510000], ltop = 0;
void insert(int x, int y){
e[++ltop]= (edge){link[x], y};
link[x] = ltop;
e[++ltop] = (edge){link[y], x};
link[y] = ltop;
}
int a[1100000], atop = 0;
int b[510000];
int dp[510000];
int v[4100000];
void dfs(int x, int y){
a[++atop] = x;
b[x] = atop;
for(int i = link[x]; i; i=e[i].next){
if(e[i].y == y)continue;
dp[e[i].y] = dp[x] + 1;
dfs(e[i].y, x);
a[++atop] = x;
}
}
int cmp(int x, int y){
if(dp[x] < dp[y])return x;
return y;
}
void build(int x, int l, int r){
if(l == r){
v[x] = a[l];
return ;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid + 1, r);
v[x] = cmp(v[x<<1], v[x<<1|1]);
}
int query(int x, int l, int r, int ql, int qr){
if(l == ql && r == qr){
return v[x];
}
int mid = (l + r) >> 1;
if(ql <= mid && mid < qr){
return cmp(query(x<<1, l, mid, ql, mid),
query(x<<1|1, mid + 1, r, mid + 1, qr));
}
else if(qr<=mid){
return query(x<<1, l, mid, ql, qr);
}
else{
return query(x<<1|1, mid + 1, r, ql, qr);
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> m >> o;
int l, r;
for(int i = 1; i < n; i++){
cin >> l >> r;
insert(l, r);
}
dfs(o, 0);
build(1, 1, atop);
while(m-- > 0){
cin >> l >> r;
if(b[l] > b[r]){
swap(l, r);
}
cout << query(1, 1, atop, b[l], b[r]) << endl;
}
return 0;
}
整体区间修改.
P3373
这一题有乘法修改和加法修改, 要弄两个懒标签数组, 一个应对乘法, 一个应对加法.
当有乘法的修改时, 我们要把加法的懒标签一并变大同等倍数, 但是加法操作的时候不需要帮乘法懒标签加上, 因为乘法标签只记录倍数, 而这个倍数对加法操作有影响.
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
ll MOD;
int n, m;
vector<ll> a(110000, 0);
vector<ll> v(410000, 0);
vector<ll> d1(410000, 1);
vector<ll> d2(410000, 0);
void build(int x, int l, int r){
if(l == r){
v[x] = a[l];
return;
}
int mid = (l + r)>>1;
build(x<<1, l, mid);
build(x<<1|1, mid + 1, r);
v[x] = (v[x<<1] + v[x<<1|1]) % MOD;
}
void mutiplyd(int x, ll z){
d1[x] = d1[x] * z % MOD;
d2[x] = d2[x] * z % MOD;
v[x] = v[x] * z % MOD;
}
void plusd(int x, int l, int r, ll z){
d2[x] = (d2[x] + z) % MOD;
v[x] = (v[x] + z * (r - l + 1)) % MOD;
}
void pushdown(int x, int l, int r, int mid){
if(d1[x] != 1){
mutiplyd(x<<1, d1[x]);
mutiplyd(x<<1|1, d1[x]);
d1[x] = 1;
}
if(d2[x] != 0){
plusd(x<<1, l, mid, d2[x]);
plusd(x<<1|1, mid + 1, r, d2[x]);
d2[x] = 0;
}
}
//y is the operation 1 is mutiply 2 is add
void modify(int x, int l, int r, int ql, int qr, int y, ll z){
if(l == ql && r == qr){
if(y == 1){
mutiplyd(x, z);
}else{
plusd(x, l, r, z);
}
return;
}
int mid = (l + r)>>1;
pushdown(x, l, r, mid);
if(ql <= mid && mid < qr){
modify(x<<1, l, mid, ql, mid, y, z);
modify(x<<1|1, mid+1, r, mid+1, qr, y, z);
}
else if(qr <= mid){
modify(x<<1, l, mid, ql, qr, y, z);
}else{
modify(x<<1|1, mid+1, r, ql, qr, y, z);
}
v[x] = (v[x<<1]+v[x<<1|1]) % MOD;
}
ll query(int x, int l, int r, int ql, int qr){
if(l == ql && r == qr){
return v[x];
}
int mid = (l + r)>>1;
pushdown(x, l, r, mid);
if(ql <= mid && qr>mid){
return (query(x<<1, l, mid, ql, mid) +
query(x<<1|1, mid + 1, r, mid + 1, qr))%MOD;
}
else if(qr <= mid){
return query(x<<1, l, mid, ql, qr);
}
else{
return query(x<<1|1, mid + 1, r, ql, qr);
}
}
int main(){
cin >> n >> m >> MOD;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
build(1, 1, n);
int l, r, k;
while(m-- > 0){
int oo;
cin >> oo;
if(oo == 1){
cin >> l >> r >> k;
modify(1, 1, n, l, r, 1, k);
}else if(oo == 2){
cin >> l >> r >> k;
modify(1, 1, n, l, r, 2, k);
}else{
cin >> l >> r;
cout << query(1, 1, n, l, r) << endl;
}
}
return 0;
}
树状数组
不用理解原理, 背了就行.
单点修改, 区间求和
int v[100000];
int lowbit(int x){return x&-x;}
int query(int x){
int sum = 0;
for(int i = x; i; i-= lowbit(i)){
sum+=v[i];
}
return sum;
}
void modify(int x, int z){
for(int i = x; i <= n; i+= lowbit(i)){
v[i] += y;
}
}
权值线段树求逆序对
什么是权值线段树呢?
就是考虑新开一些数组, 然后透过线段树对他们进行维护.
对于逆序对, 我们只需要对所有元素排个序, 然后, 我们把比 a[i] 小两倍的数的下标放进线段树, 那么一会统计的时候, 我们只需要找i + 1 到 n - 1这个范围的和就行了.
[6, 4, 3, 1]
[1, 3, 4, 6]
我们知道只有比i要小的数字在后面才有能有逆序对的可能.
所以1 不可能有逆序对产生, 而3可以有逆序对, 因为他前面有一个1比他小, 然后我们把1的下表放进线段树, 就是3, 然后我们找3的下表 + 1, 到n - 1的区间和, 你会算到1, 因为我们只把1放进去, 原理就是这样了.
vector<long long> v;
//segment tree update
void update(int l, int r, int y, int x) {
if (y < l || y > r) return;
if (l == r) {
v[x] = 1;
return;
}
int mid = l + (r - l) / 2;
if (y <= mid) update(l, mid, y, 2 * x + 1);
else update(mid + 1, r, y, 2 * x + 2);
v[x] = v[2 * x + 1] + v[2 * x + 2];
}
//segment tree range sum query
long long query(int l, int r, int ql, int qr, int x) {
if (qr < l || ql > r) {
return 0;
}
if (l >= ql && r <= qr) {
return v[x];
}
int mid = l + (r - l) / 2;
long long xx = query(l, mid, ql, qr, 2 * x + 1);
long long yy = query(mid + 1, r, ql, qr, 2 * x + 2);
return xx + yy;
}
int reversePairs(vector<int>& nums) {
int n = nums.size();
v.resize(4 * n, 0);
vector<pair<long long, long long> > vp(n);
for (int i = 0; i < n; i++) {
vp[i] = {nums[i], i};
}
sort(vp.begin(), vp.end());
long long j = 0, ans = 0;
for (int i = 0; i < n; i++) {
while (j < n && 2 * vp[j].first < vp[i].first) {
update(0, n - 1, vp[j].second, 0);
j++;
}
ans += query(0, n - 1, vp[i].second + 1, n - 1, 0);
}
return ans;
}
P2073
要同时维护最小值, 最大值, 还要支持删除操作.
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
using namespace std;
const int oo=1e9 + 7;
const int n = 1000000;
int a[1100000];
int mi[4100000], ma[4100000];
void pushup(int x){
mi[x] = min(mi[x<<1], mi[x<<1|1]);
ma[x] = max(ma[x<<1], ma[x<<1|1]);
}
void build(int x, int l, int r){
if(l == r){
mi[x] = oo;
ma[x] = 0;
return;
}
int mid = (l + r)>>1;
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
pushup(x);
}
void add(int x, int l, int r, int y){
if(l == r){
mi[x] = l;
ma[x] = l;
return;
}
int mid = (l + r)>>1;
if(y <= mid)add(x<<1, l, mid, y);
else add(x<<1|1, mid+1, r, y);
pushup(x);
}
void remove(int x, int l, int r, int y){
if(l == r){
mi[x] = oo;
ma[x] = 0;
return;
}
int mid = (l + r)>>1;
if(y <= mid) remove(x<<1, l, mid, y);
else remove(x<<1|1, mid+1, r, y);
pushup(x);
}
int main(){
build(1, 1, n);
int k, l, r;
long long sum1 =0, sum2=0;
while(1){
cin >> k;
cout << k;
if(k == 1){
cin >> l >> r;
if(a[r] == 0){
a[r] = l;
sum1+=l;
sum2+=r;
add(1, 1, n, r);
}
}else if(k == 3){
if(mi[1] == oo)continue;
sum1 -= a[mi[1]];
sum2 -= mi[1];
a[mi[1]] = 0;
remove(1, 1, n, mi[1]);
}else if (k == 2){
if(ma[1] == 0)continue;
sum1-=a[ma[1]];
sum2-=ma[1];
a[ma[1]] = 0;
remove(1, 1, n, ma[1]);
}else{
cout << sum1 << " " << sum2 << endl;
return 0;
}
}
}