树状数组(1)

1.树状数组 1

在这里插入图片描述

思路

单点修改,区间查询

#include<iostream>
using namespace std;
const int N = 5e5 + 10;
int a[N], c[N];
int n, m;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

int query(int x){
	int res = 0;
	for(int i = x; i >= 1; i -= lowbit(i)) res += c[i];
	return res;
}

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%d", &a[i]);
		add(i, a[i]);
	}
	int z, x, y;
	while(m--){
		scanf("%d", &z);
		if(z == 1){
			scanf("%d%d", &x, &y);
			add(x, y);
		}else{
			scanf("%d%d", &x, &y);
			printf("%d\n", query(y) - query(x - 1));
		}
	}
	return 0;
}

2.简单题

在这里插入图片描述

思路

区间修改,单点查询,a ^ b = c -> a ^ c = b
考虑差分序列(用异或代替减法),则每个元素的真实值是差分序列的异或前缀和,修改的时候只需要改 l 和 r + 1 两个位置的差分

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], c[N];
int n, m;

int lowbit(int x){
	return x & -x;
}

void add(int x){
	for(int i = x; i <= n; i += lowbit(i)) c[i] ^= 1;
}

int query(int x){
	int res = 0;
	for(int i = x; i >= 1; i -= lowbit(i)) res ^= c[i];
	return res;
}

int main(){
	scanf("%d%d", &n, &m);
//	for(int i = 1; i <= n; i++){
//		scanf("%d", &a[i]);
//		add(i, a[i]);
//	}
	int z, x, y;
	while(m--){
		scanf("%d", &z);
		if(z == 1){
			scanf("%d%d", &x, &y);
			add(x);
			add(y + 1);
		}else{
			scanf("%d", &x);
			printf("%d\n", query(x));
		}
	}
	return 0;
}

3.树状数组 2

在这里插入图片描述

思路

区间修改,单点查询,用差分数组维护每次的操作,然后再加上原数组

#include<iostream>
using namespace std;
const int N = 5e5 + 10;
int a[N], c[N];
int n, m;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

int query(int x){
	int res = 0;
	for(int i = x; i >= 1; i -= lowbit(i)) res += c[i];
	return res;
}

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	int z, x, y, k;
	while(m--){
		scanf("%d", &z);
		if(z == 1){
			scanf("%d%d%d", &x, &y, &k);
			add(x, k);
			add(y + 1, -k);
		}else{
			scanf("%d", &x);
			printf("%d\n", a[x] + query(x));
		}
	}
	return 0;
}

4.统计和

在这里插入图片描述

思路

单点修改,区间查询,记得开 long long

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
long long a[N], c[N];
int n, m;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

long long query(int x){
	long long res = 0;
	for(int i = x; i >= 1; i -= lowbit(i)) res += c[i];
	return res;
}

int main(){
	scanf("%d%d", &n, &m);
	//for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	char z;
	int x, y;
	while(m--){
		cin>>z;
		if(z == 'x'){
			cin>>x>>y;
			//scanf("%d%d", &x, &y);
			add(x, y);
		}else{
			cin>>x>>y;
			//scanf("%d%d", &x, &y);
			printf("%lld\n", query(y) - query(x - 1));
		}
	}
	return 0;
}

5.Agent2

在这里插入图片描述

思路

区间修改,单点查询

#include<iostream>
using namespace std;
const int N = 1e7 + 10;
int a[N], c[N];
int n, m;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

int query(int x){
	int res = 0;
	for(int i = x; i >= 1; i -= lowbit(i)) res += c[i];
	return res;
}

int main(){
	scanf("%d%d", &n, &m);
	//for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	//char z;
	int z, x, y;
	while(m--){
		//cin>>z;
		scanf("%d", &z);
		if(z == 0){
			//cin>>x>>y;
			scanf("%d%d", &x, &y);
			add(x, 1);
			add(y + 1, -1);
		}else{
			//cin>>x>>y;
			scanf("%d", &x);
			printf("%d\n", query(x));
		}
	}
	return 0;
}

6.Balanced Lineup G

在这里插入图片描述

思路

维护区间最大值和最小值
在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 5e4 + 10;
int a[N], c[N];
int fmax[N], fmin[N];
int n, m;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

int query(int x){
	int res = 0;
	for(int i = x; i >= 1; i -= lowbit(i)) res += c[i];
	return res;
}

// 建树
void ff(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)){
		fmax[i] = max(fmax[i], y);
		fmin[i] = min(fmin[i], y);
	}
}

// 求区间最大值 
int query1(int x, int y){
	if(y > x){
		if(y - lowbit(y) > x) return max(fmax[y], query1(x, y - lowbit(y)));
		else return max(a[y], query1(x, y - 1));
	}
	return a[x];
}

// 求区间最小值 
int query2(int x, int y){
	if(y > x){
		if(y - lowbit(y) > x) return min(fmin[y], query2(x, y - lowbit(y)));
		else return min(a[y], query2(x, y - 1));
	}
	return a[x];
}

int main(){
	memset(fmin, 0x3f, sizeof(fmin));
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%d", &a[i]);
		ff(i, a[i]);
	}
	//char z;
	int z, x, y;
	while(m--){
		//cin>>z;
//		scanf("%d", &z);
//		if(z == 0){
//			//cin>>x>>y;
//			scanf("%d%d", &x, &y);
//			add(x, 1);
//			add(y + 1, -1);
//		}else{
			//cin>>x>>y;
			scanf("%d%d", &x, &y);
			printf("%d\n", query1(x, y) - query2(x, y));
//		}
	}
	return 0;
}

7.异或橙子

在这里插入图片描述

思路

单点修改,区间查询
如果区间左右端点奇偶性不同,那就为 0,因为刚好可以两两抵消,否则就分开计算偶数和奇数的异或和

#include<iostream>
using namespace std;
const int N = 2e5 + 10;
int a[N];
int n, m;

// 用结构体同时存储奇偶位置 
struct Node{
	int c[N] = {0};
	int lowbit(int x){
		return x & -x;
	}
	void add(int x, int y){
		for(int i = x; i <= n; i += lowbit(i)) c[i] ^= y;
	}
	int query(int x){
		int res = 0;
		for(int i = x; i >= 1; i -= lowbit(i)) res ^= c[i];
		return res;
	}
}tr[2];

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%d", &a[i]);
		tr[1 & i].add(i, a[i]);
	}
	//char z;
	int z, x, y;
	while(m--){
		//cin>>z;
		scanf("%d", &z);
		if(z == 1){
			//cin>>x>>y;
			scanf("%d%d", &x, &y);
			tr[x & 1].add(x, a[x] ^ y);
			a[x] = y;
		}else{
			//cin>>x>>y;
			scanf("%d%d", &x, &y);
			if((x & 1) != (y & 1)) printf("0\n");
			else printf("%d\n", tr[x & 1].query(x - 1) ^ tr[x & 1].query(y));
		}
	}
	return 0;
}

8.逆序对

在这里插入图片描述

思路

先离散化,然后每次找到前面比当前大的数,数值作为树状数组下标

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 5e5 + 10;
vector<int> a, all;
int c[N];
int n;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

int query(int x){
	int res = 0;
	for(int i = x; i > 0; i -= lowbit(i)) res += c[i];
	return res;
}

int find(int x)
{
    int l = -1, r = all.size();
    while(l + 1 < r)
    {
        int mid = l + r >> 1 ;
        if(all[mid] <= x) l = mid ;
        else r = mid;
    }
    return r;
}

int main(){
	scanf("%d", &n);
	int x;
	for(int i = 0; i < n; i++){
	    scanf("%d", &x);
	    a.push_back(x);
	    all.push_back(x);
	}
	// 离散化
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	// 求前面有多少个比当前大 
	long long sum = 0;
	for(int i = 0; i < n; i++){
		// 后面的就是比当前数大的,数值作为下标 
		int y = find(a[i]);
		sum += query(n) - query(y);
		add(y, 1);
	}
	printf("%lld", sum);
	return 0;
}

9.最接近神的人

在这里插入图片描述

思路

每次只能交换相邻的两个,交换完一次,逆序对数量就减一,所以就是求逆序对数

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 5e5 + 10;
vector<int> a, all;
int c[N];
int n;

int lowbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i = x; i <= n; i += lowbit(i)) c[i] += y;
}

int query(int x){
	int res = 0;
	for(int i = x; i > 0; i -= lowbit(i)) res += c[i];
	return res;
}

int find(int x)
{
    int l = -1, r = all.size();
    while(l + 1 < r)
    {
        int mid = l + r >> 1 ;
        if(all[mid] <= x) l = mid ;
        else r = mid;
    }
    return r;
}

int main(){
	scanf("%d", &n);
	int x;
	for(int i = 0; i < n; i++){
	    scanf("%d", &x);
	    a.push_back(x);
	    all.push_back(x);
	}
	// 离散化
	sort(all.begin(), all.end());
	// 求前面有多少个比当前大 
	long long sum = 0;
	for(int i = 0; i < n; i++){
		// 后面的就是比当前数大的,数值作为下标 
		int y = find(a[i]);
		sum += query(n) - query(y);
		add(y, 1);
	}
	printf("%lld", sum);
	return 0;
}

10.Out of Sorts S

在这里插入图片描述

思路

根据规则,每一次操作中,原数列中的数相对于目标数列中的数,向左的每次只向左一位,向右的每一次向右多位,因此答案只和向左次数最多的那一对有关。答案就是向左最多次数那一对的位置差+1。为什么要+1呢?因为最后一次就算序列有序的话仍然要进去所以要输出一次moo。

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
pair<int, int> a[N];
int n;

int main(){
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		scanf("%d", &a[i].first);
		a[i].second = i;
	}
	sort(a, a + n);
	int ans = 0;
	for(int i = 0; i < n; i++) ans = max(ans, a[i].second - i);
	printf("%d", ans + 1);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值