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;
}