树状数组 单点修改 区间查询(查询从1到k的属性 )
单调修改区间查询(两者相减)
离散化 +树状数组 +LIS
最大上升子序列和
需要查询
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 100010;
int n;
int w[N];
LL tr[N];
vector<int> xs;
LL f[N];
int get(int x)
{
return lower_bound(xs.begin(), xs.end(), x) - xs.begin() + 1;//存是在0开始存的 但返回的时候是1开始返回的
}
int lowbit(int x)
{
return x & -x;
}
void add(int x, LL v)
{
for (int i = x; i <= n; i += lowbit(i))
tr[i] = max(tr[i], v);
}
LL query(int x)
{
LL res = 0;
for (int i = x; i; i -= lowbit(i))
res = max(res, tr[i]);
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &w[i]);
xs.push_back(w[i]);//保存所有存储的元素
}
sort(xs.begin(), xs.end());//将所有元素大小排序
xs.erase(unique(xs.begin(), xs.end()), xs.end());//删除重复的元素 这里的xs还是存的元素而不是下表 用于find的找
LL res = 0;
for (int i = 0; i < n; i ++ )//这里的数是n个a数组
{
int k = get(w[i]);//找到w[i]元素在xs数组对应的映射的下标
f[i] = query(k - 1) + w[i];//query查询xs的下标
res = max(res, f[i]);//更新最大值
add(k, f[i]);//在tr第k个位置插入
}
printf("%lld\n", res);
return 0;
}
楼兰图腾https://www.acwing.com/problem/content/243/
看看某个点的高度 他的左边和右边的点有多少个点比他高的
tr[i]为i下标点的高度 每次查询完add对某个高度 +1
就可以完成看看左边的操作
看左边的高度 从1~n 看右边的高度 从n~1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5+10;
int tr[N],a[N];
int great[N],lower[N];
int lowbit (int x){
return x&-x;
}
void add(int x,int v){
for (int i = x; i <N; i +=lowbit(i) ) tr[i]+=v;
}
int query(int x){
int res=0;
for (int i = x; i ; i -=lowbit(i) ){
res+=tr[i];
}
return res;
}
signed main()
{
int n;
cin>>n;
for (int i = 1; i <= n; i ++ ){
cin >> a[i];
}
for (int i = 1; i <= n; i ++ ){
great[i]+=query(n)-query(a[i]);
lower[i]+=query(a[i]-1);
add(a[i],1);//tr[i] i小标表示的是高度
}
memset(tr, 0, sizeof tr);
int res=0,ans=0;
for (int i = n; i >=1 ; i -- ){
res+=great[i]*(query(n)-query(a[i]));
ans+=lower[i]*(query(a[i]-1));
add(a[i], 1);
}
cout << res<<" "<<ans;
return 0;
}
数星星https://www.acwing.com/problem/content/1267/
还是看看左边的高度
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 32005;
int n;
int tr[N];
int lowbit(int x){
return x&-x;
}
void add(int x,int v){
for (int i = x; i < N; i +=lowbit(i) ){
tr[i]+=v;
}
// return ;
}
int query(int x){
int res=0;
for (int i = x; i; i -=lowbit(i) ){
res+=tr[i];
}
return res;
}
int q[N];
signed main(){
cin>>n;
for (int i = 1; i <= n; i ++ ){
int x,y;scanf("%d%d",&x,&y);x++;
// cout << query(x)<<endl;
q[query(x)]++;
add(x,1 );
}
for (int i = 0; i < n; i ++ ){
// cout <<q[i] <<"\n";
printf("%d\n",q[i]);
}
return 0;
}
区间修改 单点查询
一个简单的整数的问题
https://www.acwing.com/problem/content/248/
区间加数: 将原数组转为差分数组 tr[]里面存储的是a[]的差分数组 然后再对两个端点进行经典的差分数组加数
一个简单的整数问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int tr[N],a[N];
int n,m;
int lowbit(int x){
return x&-x;
}
void add(int x,int v){
for (int i = x; i <= n; i +=lowbit(i) ){
tr[i]+=v;
}
}
int ask(int x){
int res=0;
for (int i = x; i ; i -=lowbit(i) ){
res+=tr[i];
}
return res;
}
int main()
{
cin >> n>>m;
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) add(i,a[i]-a[i-1]);
for (int i = 0; i < m; i ++ ){
string s;
cin >> s;
if(s=="C"){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
add(a, c);add(b+1,-c);
}else{
int a;cin>>a;
cout << ask(a)<<endl;//对差分数组求和的值就是这个点的的值
}
}
return 0;
}
区间修改 区间查询(与上面的区别主要在修改 差分的前缀和的前缀和)
原理如图 主要通过在加一行(b1..bx)和一个tr2[i]数组来处理出相关内容
tr1[1]//b[i]的前缀和
tr2[]//b[i]i的前缀和
与上面的区间修改单点查询大部分内容相同
要注意 修改的时候 每次需要修改两个数组的差分端点 一个数组是l,d和r+1,d ,一个是ld,(r+1)*d
区间查询代码:
LL prefix_sum(int x){
return sum(tr1,x)*(x+1)-sum(tr2,x);//两个sum是一般的操作代码 需要传出数组名到第是哪个地方需要
}
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, m;
int a[N];
LL tr1[N]; // 维护b[i]的前缀和
LL tr2[N]; // 维护b[i] * i的前缀和
int lowbit(int x)
{
return x & -x;
}
void add(LL tr[], int x, LL c)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
LL sum(LL tr[], int x)
{
LL res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
LL prefix_sum(int x)
{
return sum(tr1, x) * (x + 1) - sum(tr2, x);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ )
{
int b = a[i] - a[i - 1];
add(tr1, i, b);
add(tr2, i, (LL)b * i);
}
while (m -- )
{
char op[2];
int l, r, d;
scanf("%s%d%d", op, &l, &r);
if (*op == 'Q')
{
printf("%lld\n", prefix_sum(r) - prefix_sum(l - 1));
}
else
{
scanf("%d", &d);
// a[l] += d
add(tr1, l, d), add(tr2, l, l * d);
// a[r + 1] -= d
add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);
}
}
return 0;
}