线段树 7.25杭电多校赛 G

7.

Naive Operations

Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 502768/502768 K (Java/Others)Total Submission(s): 1899 Accepted Submission(s): 258

 

Problem Description

In a galaxy far, far away, there are two integer sequence a and b of length n.b is a static permutation of 1 to n. Initially a is filled with zeroes.There are two kind of operations:

  1. add l r: add one for ​

  2. query l r: query ​

 

Input

There are multiple test cases, please read till the end of input file.For each test case, in the first line, two integers n,q, representing the length of a,b and the number of queries.In the second line, n integers separated by spaces, representing permutation b.In the following q lines, each line is either in the form 'add l r' or 'query l r', representing an operation.1≤n,q≤100000, 1≤l≤r≤n, there're no more than 5 test cases.

 

Output

Output the answer for each 'query', each one line.

 

Sample Input

5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5

 

Sample Output

1
1
2
4
4
6

 

题目大意:

有n个数的全排列b,与a等长且初始为0的数组a

q次操作,两种:

add L R:[L,R]内ai++。

query L R: 查询​

想法:

​ q次在线查询,q<=1e5,那么里面这两种操作都至少要logn的复杂度来实现了。区间操作,先想到了树状数组,可是这没办法实现第二种操作区间更新。 使用线段树,做延迟标记就可以区间更新了。

思路:

1、线段树,节点维护两个值:区间内​之和s,区间最小更新代价mv。当区间更新时,若区间节点mv为0,则向下找到mv为0的那个节点,将其s加一。至于更新后维护区间统一就靠延迟标记fg了。

线段树:

https://www.cnblogs.com/TenosDoIt/p/3453089.html

https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
// head
​
const int N=101000;
struct node {
    int fg;                         //mv更新的延迟标记 
    int s;                          //ai/bi取整值的和  
    int mv;                         //最小更新代价 
}nd[4*N];
int b[N],n,m,l,r;
char s[20];
​
void upd(int p) {               //更新节点的值 
    nd[p].mv=min(nd[p+p].mv,nd[p+p+1].mv);      //区间内ai最小值是子树中的ai最小值 
    nd[p].s=nd[p+p].s+nd[p+p+1].s;              //区间内ai/bi取整值的和 
}
void setf(int p,int f) {         //设置更新标记 
    nd[p].fg+=f;
    nd[p].mv+=f;                
}
void build(int p,int l,int r) {
    nd[p].fg=0;
    if (l==r) {                 //当前节点是叶子 
        nd[p].mv=b[l]-1;        //结点初始的ai值是b[i]-1 
        nd[p].s=0;              //ai/bi取整值是0 
    } else {
        int md=(l+r)>>1;        //取中间 
        build(p+p,l,md);        //递归构造左子树
        build(p+p+1,md+1,r);    //递归构造右子树
        upd(p);                 //更新当前根节点的值 
    }
}
void push(int p) {
    if (nd[p].fg) {                 //当前节点有更新标记值 
        setf(p+p,nd[p].fg);         //更新子区间的更新标记值
        setf(p+p+1,nd[p].fg);       
        nd[p].fg=0;                 //去掉更新标记值 
    }
}
int query(int p,int l,int r,int tl,int tr) {
    if (tl==l&&tr==r) return nd[p].s;           //正好查询的是该区间,返回查询结果 
    else {                                      //如果不是,就向下查询 
        push(p);
        int md=(l+r)>>1;
        if (tr<=md) return query(p+p,l,md,tl,tr);
        else if (tl>md) return query(p+p+1,md+1,r,tl,tr);
        else return query(p+p,l,md,tl,md)+query(p+p+1,md+1,r,md+1,tr);
    }
}
void modify(int p,int l,int r,int tl,int tr) {
    if (tl>tr) return;
//  printf("modify %d %d %d %d %d %d\n",l,r,tl,tr,nd[p].mv,nd[p].s);
    if (tl==l&&tr==r) {             //找到该区间 
        if (nd[p].mv>0) {           //区间内还没有可以更新的 
            nd[p].mv--;             //更新区间最小更新代价 
            nd[p].fg--;
        } else {                    //区间有可以更新的了 
            if (tl==tr) {           //如果是个节点 
                nd[p].mv=b[l]-1;    //重设最小值 
                nd[p].s++;          //ai/bi取整值和++
            } else {
                push(p);
                int md=(l+r)>>1;
                if (nd[p+p].mv==0) modify(p+p,l,md,tl,md);
                    else setf(p+p,-1);              //设置子节点更新标记 
                if (nd[p+p+1].mv==0) modify(p+p+1,md+1,r,md+1,tr);
                    else setf(p+p+1,-1);
                upd(p);
            }
        }
    } else {
        push(p);
        int md=(l+r)>>1;
        if (tr<=md) modify(p+p,l,md,tl,tr);
        else if (tl>md) modify(p+p+1,md+1,r,tl,tr);
        else modify(p+p,l,md,tl,md),modify(p+p+1,md+1,r,md+1,tr);
        upd(p);
    }
//  printf("ff %d %d\n",p,nd[p].s);
}
​
int main() {
    while (scanf("%d%d",&n,&m)!=EOF) {
        rep(i,1,n+1) scanf("%d",b+i);
        build(1,1,n);
        rep(i,1,m+1) {
            scanf("%s%d%d",s,&l,&r);
            if (s[0]=='a') {
                modify(1,1,n,l,r);              //l,r区间内加一 
            } else {
                printf("%d\n",query(1,1,n,l,r));
            }
        }
    }
}

 

参考:

https://www.ideone.com/Wo55gi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值