7.
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:
add l r: add one for
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
#include
using namespace std;
#define rep(i,a,n) for (int i=a;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 VI;
typedef long long ll;
typedef pair 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