1.树状数组
树状数组支持动态维护一个数组,支持单点修改,区间求和,区间修改
本文只给出例题与参考资料
树状数组经典图片
参考博客
参考视频
楼兰图腾
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200009;
int high[N],low[N];
int n;
int a[N];
int tr[N];
signed lowbit(int x){
return x&(-x);
}
signed add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)){
tr[i]+=k;
}
}
signed ask(int x){
int sum=0;
for(int i=x;i>0;i-=lowbit(i)){
sum+=tr[i];
}
return sum;
}
signed main(){
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++){
int x=a[i];
//统计在区间1~x-1的数的个数,即a[1]~a[i-1]中比a[i]小的数的个数
low[i]=ask(x-1);
//统计在区间x+1~n的数的个数,即a[1]~a[i-1]中比a[i]大的数的个数
high[i]=ask(n)-ask(x);
add(x,1); //在x位置上加1代表大小为x的有一个数,建立树状数组
}
int res1=0,res2=0;
memset(tr,0,sizeof tr);
for(int i=n;i>=1;i--){
int x=a[i];
//统计在区间1~x-1的数的个数,即a[i+1]~a[n]中比a[i]小的数的个数
int low2=ask(x-1);
//统计在区间x+1~n的数的个数,即a[i+1]~a[n]中比a[i]大的数的个数
int high2=ask(n)-ask(x);
add(x,1);
res1+=high[i]*high2;
res2+=low[i]*low2;
}
cout << res1 << " " << res2;
return 0;
}
一个简单的整数问题
// 树状数组维护差分数组,区间修改 ,单点查询 ,差分,注意差分的原理应用不要弄混
#include<iostream>
#define int long long
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
int tr[N];
int lowbit(int x){
return x&(-x);
}
int sum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k;
}
signed main(){
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> a[i];
}
for(int i=1;i<=n;i++){
add(i,a[i]-a[i-1]);
}
while(m--){
char q;
cin >> q;
if(q=='Q'){
int x;
cin >> x;//单点查找
cout << sum(x) << endl;
}
else {
int l,r,c;
cin >> l >> r >> c;
add(r+1,-c),add(l,c);
}
}
return 0;
}
一个简单的整数问题2
//区间修改 ,区间求和
//维护两个树状数组 一个维护a[i]的差分 ,一个维护i*a[i]的差分
//对于区间修改 和上题一样 利用差分数组的性质即可
//对于区间求和 我们知道a[i]=b[1]+....+b[i],则前缀和s[i]=a[i]+...+a[i]=i*b[1]+(i-1)*b[2]+...+b[i]
/// 可以化为s[i]=(i+1)(b[1]+b[2]+..b[i])-(1*b[1]+2*b[2]+...+i*b[i]);
#include<iostream>
#define int long long
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
int tr1[N],tr2[N];
int lowbit(int x){
return x&(-x);
}
void add(int tr[],int x,int k){
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k;
}
int sum(int tr[],int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
signed main(){
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> a[i];
add(tr1,i,a[i]-a[i-1]);
add(tr2,i,i*a[i]-i*a[i-1]);
}
while(m--){
char q;
cin >> q;
if(q=='Q'){ //求区间和
int l,r;
cin >> l >> r;
cout << ((r+1)*sum(tr1,r)-sum(tr2,r))-(l*sum(tr1,l-1)-sum(tr2,l-1))<<endl;
}
else { //区间修改
int l,r,c;
cin >> l >> r >> c;
add(tr1,r+1,-c),add(tr1,l,c);
add(tr2,r+1,-c*(r+1)),add(tr2,l,c*l);
}
}
return 0;
}
谜一样的牛
//我们从后往前看,假设最后一个数为x,代表前面有x个比他小的数,则在第X这个
//位置上 应该是第X+1小的数 我们可以利用二分求出来这个数,因为每个数只出现
//一次所以用完一个数后要给这个数置0,a[x]=1 表示 X这个数还能用,
//sum[x]=k 表示前X个数其中有k个还没用过,我们二分的就应该是SUM数组,
//假设第i头 前面有q个比他大的数那么我们应该从SUM中找出大于等于q+1的最小值
//那么这头牛的高度就应该为二分出来的那个下标+1的数
//比如 当前牛前面有6个比他低的 ,我们二分出来 sum[9]=7,表示前9个数有7个
//还没用过,这就说明了前面的牛必全是前8个数里面的,当前牛的高度必为9
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n;
int a[N];
int h[N];
int tr[N];
int lowbit(int x){
return x&(-x);
}
void add(int x,int c){
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
int sum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
int main(){
cin >> n;
for(int i=2;i<=n;i++){
cin >> a[i];
add(i,1);
}
a[1]=0;
add(1,1);
for(int i=n;i>=1;i--){
// int k=a[i]+1;
int l=1,r=n;
while(l<r){
int mid=l+r>>1;
if(sum(mid)>=a[i]+1){
r=mid;
}
else {
l=mid+1;
}
}
h[i]=r;
add(r,-1);
}
for(int i=1;i<=n;i++) {
cout << h[i] << endl;
}
return 0;
}
逆序对
//对于每一个数统计一下这个数后面有多少个数比他小,离散化加树状数组
#include<iostream>
#include<unordered_map>
#include<algorithm>
#define int long long
using namespace std;
const int N=5e5+10;
int a[N],lsh[N];
int n,m;
int tr[N];
int lowbit(int x){
return x&(-x);
}
void add(int x,int k){
for(int i=x;i<=N;i+=lowbit(i)) tr[i]+=k; //注意啊这里的循环终止条件是N 他应该是树状数组的最大下标
}
int sum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
signed main(){
cin >> n;
int res=0;
for(int i=1;i<=n;i++) cin >> a[i],lsh[i]=a[i];
sort(lsh+1 , lsh+n+1);
int cnt = unique(lsh+1 , lsh+n+1) - lsh - 1;//unique会删除相邻的重复的元素,其实
//并不是真的删除而是把这些元素放到数组的最后面了,这里的cnt就是去重后数组的长度
for(int i=1; i<=n; i++){
a[i] = lower_bound(lsh+1 , lsh+cnt+1 , a[i]) - lsh;//lower_bound返回的是大于等于a[i]的第一个数
//的位置 因为是位置 所以要减去lsh
}
//至此离散化结束
for(int i=n;i>=1;i--){
res+=sum(a[i]-1);
add(a[i],1);
}
cout << res;
return 0;
}
关于离散化
这里还有一个关于离散化的例题
区间和
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=3e5+10;
int n,m;
int a[N],lsh[N],sum[N],num[N];
pair<int,int>v[N];
pair<int,int>p[N];
int main(){
cin >>n >>m;
int cnt1=1,cnt2=1,cnt3=1;
for(int i=1;i<=n;i++){
int x,c;
cin >>x >> c;
a[cnt1++]=x;
lsh[cnt3++]=x;
p[i]={x,c};
}
for(int i=1;i<=m;i++){
int l,r;
cin >> l >> r;
v[i]={l,r};
a[cnt1++]=l;
a[cnt1++]=r;
lsh[cnt3++]=l;
lsh[cnt3++]=r;
}
// for(int i=1;i<cnt1;i++){
// cout << a[i] << " ";
// }
// cout << endl;
sort(a+1,a+cnt1);
// for(int i=1;i<cnt1;i++){
// cout << a[i] << " ";
// }
// cout << endl;
int cnt=unique(a+1,a+cnt1)-1-a;
// for(int i=1;i<cnt1;i++){
// cout << a[i] << " ";
// }
// cout << endl;
for(int i=1;i<cnt1;i++){
lsh[i]=lower_bound(a+1,a+cnt+1,lsh[i])-a;
}
// for(int i=1;i<cnt1;i++){
// // cout << a[i] << " ";
// cout << lsh[i] << endl;
// }
for(int i=1;i<=n;i++){
num[lsh[i]]+=p[i].y;
}
for(int i=1;i<cnt1;i++){
sum[i]=sum[i-1]+num[i];
// cout << num[i] << endl;
}
for(int i=1;i<=m;i++){
int l=lower_bound(a+1,a+cnt+1,v[i].x)-a;
int r=lower_bound(a+1,a+cnt+1,v[i].y)-a;
// cout << v[i].x << " " << v[i].y << " ";
// cout << l << " " << r << endl;
cout << sum[r]-sum[l-1]<<endl;
}
return 0;
}
火柴人排队
//有一点数论 就是比如说a>b>c,e>d>f 那么从这6个数中两个数为一对相乘再相加,
//那么一定是 a*e+b*d+c*f 最大
//我们平常进行离散化 离散化得到新数组 假如a[5]=7,代表第5个位置上放的是第7小的
//但是本题离散化后应该要求得到 a[5]=7 第5小的数放在第7个位置上
//这个题要求我们的是 要把第一个数组第x位置上应该放第n小的 ,第二个数组第x个位置
//上也应该放第n小的,我们为了实现这一要求,新建数组q[a[i]]=b[j] ,代表当前a数组第i小的
//数的位置对应的是b数组第j小的位置,比如q[5]=7,代表了当前a数组第5小的位置对应的是
//b数组第7小的位置,我们想让a数组第5小的位置对应的是b数组第5小的位置
//即我们想让a[i]==b[j],即我们要让q数组满足一一对应,q[1]=1,q[2]=2 ......
//所以我们让q数组升序 再求逆序对即可
#include<iostream>
#include<unordered_map>
#include<algorithm>
#define int long long
using namespace std;
const int N=5e5+10,mod=1e8-3;
struct Node {
int num,id;
}a[N],b[N];
int n,m;
int tr[N],q[N];
int lowbit(int x){
return x&(-x);
}
void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k;
}
int sum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
bool cmp(Node x, Node y)
{
return x.num < y.num;
}
signed main(){
cin >> n;
int res=0;
for(int i=1;i<=n;i++) cin >> a[i].num,a[i].id=i;
for(int i=1;i<=n;i++) cin >> b[i].num,b[i].id=i;
sort(a+1,a+n+1,cmp);// 结构体排序必须得写一个cmp?
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++){
q[a[i].id]=b[i].id;
}
for(int i=n;i>=1;i--){
res=(res+sum(q[i]-1))%mod; // sum(6) 0 sum(9)
add(q[i],1); //add(7,1)
}
cout << res;
return 0;
}
配对统计
// 关于本题 有以下几点提示或说明
//1.sort结构体时要自己写一个cmp不然会报错
//2.注意预处理的思想,本题充分利用了预处理思想,预处理了所有可能出现的好对
//3.本题注意定义的结构体abc,要带一个id本题都是利用每个数组的编号取进行维护,而不是数组本身
//数组本身进行了排序之后已经失去了它本身的意义,所以要保留编号,而且询问时询问
//的也是编号
//具体思路见链接,有点像贪心里的区间问题?
#include<iostream>
#include<vector>
#include<algorithm>
#define x first
#define y second
#define int long long
using namespace std;
const int N=3e5+10;
int n,m;
int tr[N];
vector<pair<int,int>>v;
struct abc{
int num,id;
}a[N];
struct Node{
int l,r,id;
}quesiton[N];
bool cmp(Node x,Node y){
if(x.r!=y.r) return x.r<y.r;
return x.l<y.l;
}
int lowbit(int x){
return x&-x;
}
void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k;
}
int sum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
bool cmpp(pair<int,int>a,pair<int,int>b){
if(a.y!=b.y) return a.y<b.y;
return a.x<b.x;
}
bool cmppp(abc a1,abc a2){
return a1.num<a2.num;
}
void wuhu(abc a1,abc a2){
int l=min(a1.id,a2.id);
int r=max(a1.id,a2.id);
v.push_back({l,r});
}
signed main(){
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i].num,a[i].id=i;
if(n==1) { //针对被hack的一个数据 特判一下
cout << 0;
return 0;
}
//预处理所有好对
sort(a+1,a+n+1,cmppp);
wuhu(a[1],a[2]);
wuhu(a[n-1],a[n]);
for(int i=2;i<n;i++){
int dis1=abs(a[i].num-a[i-1].num),dis2=abs(a[i].num-a[i+1].num);
if(dis1<dis2) wuhu(a[i-1],a[i]);
else if(dis2==dis1) wuhu(a[i-1],a[i]),wuhu(a[i],a[i+1]);
else wuhu(a[i],a[i+1]);
}
sort(v.begin(),v.end(),cmpp);
//预处理完毕
//预处理 所有询问,并将所有询问按右端点进行从小到大排序
for(int i=1;i<=m;i++){
int l,r;
cin >> l >> r;
quesiton[i]={l,r,i};
}
sort(quesiton+1,quesiton+1+m,cmp);
//预处理完毕 开始询问
int ans=0;
for(int i=1,j=0;i<=m;i++){
while(v[j].y<=quesiton[i].r&&j<v.size()) add(v[j].x,1),j++; // 加入好对
ans+=quesiton[i].id*(sum(quesiton[i].r)-sum(quesiton[i].l-1));
}
cout << ans;
return 0;
}
2.线段树
最大数
//线段数 基础四个操作:建树,单点修改,区间查询,pushup
#include<iostream>
#define int long long
using namespace std;
const int N=2e5+10;
int n,m,p,a;
struct Node{
int l,r,maxv;
}tr[N*4];
void build(int u,int l,int r){
tr[u]={l,r,0};
if(l==r) return;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
void modify(int u,int k,int c){
if(tr[u].l==k&&tr[u].r==k){
tr[u].maxv=c;
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(mid>=k){
modify(u<<1,k,c);
}
else{
modify(u<<1|1,k,c);
}
tr[u].maxv=max(tr[u<<1].maxv,tr[u<<1|1].maxv);
}
int query(int u,int l,int r){
int ans=0;
if(l<=tr[u].l&&r>=tr[u].r){
ans=max(ans,tr[u].maxv);
return ans;
}
int mid=tr[u].l+tr[u].r>>1;
if(mid>=l){
ans=max(ans,query(u<<1,l,r));
}
if(mid<r){ // 注意这里一定不要再带=号了 一定要与建树的时候保持一致
//建树的时候 把mid分给了左半边,如果这里再加等号,就回去右半边去找mid,很显然找不到会死循环
ans=max(ans,query(u<<1|1,l,r));
}
return ans;
}
signed main(){
cin >> m >> p;
build(1,1,m);
while(m--){
char c;
int x;
cin >> c >> x;
if(c=='A'){
modify(1,n+1,(x+a)%p);
// cout << "tmp" << n<<endl;
n++;
}
else {
a=query(1,n-x+1,n);
cout << a << endl;
}
}
return 0;
}
你能回答这些问题吗
// yes i can
//单点修改,区间查询
#include<iostream>
using namespace std;
const int N=5e5+10;
int n,m;
int a[N];
struct Node{
int l,r,trm,sum,suml,sumr; // 左边界,右边界,最大连续和,区间和,前缀和,后缀和
}tr[N*4];
void pushup(Node &u,Node &sonl,Node &sonr){
u.sum=sonl.sum+sonr.sum;
u.suml=max(sonl.suml,sonl.sum+sonr.suml);
u.sumr=max(sonr.sumr,sonr.sum+sonl.sumr);
u.trm=max(max(sonl.trm,sonr.trm),sonl.sumr+sonr.suml);
}
void pushup(int u){
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r) {
tr[u]={l,r,a[r],a[r],a[r],a[r]}; // 叶子节点
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
Node query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u];
}
Node res;
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid){
return query(u<<1,l,r);
}
else if(l>mid){
return query(u<<1|1,l,r);
}
Node sonl=query(u<<1,l,r);
Node sonr=query(u<<1|1,l,r);
pushup(res,sonl,sonr);
return res;
}
// 当然query也可以这样写 但因为本题返回的是Node 所以按上面的写比较方便
// Node query(int u,int l,int r){
// if(tr[u].l>=l&&tr[u].r<=r){
// return tr[u];
// }
// Node res,sonl,sonr;
// int mid=tr[u].l+tr[u].r>>1;
// if(l<=mid&&r<=mid){
// return query(u<<1,l,r);
// }
// else if(r>mid&&l>mid){
// return query(u<<1|1,l,r);
// }
// else {
// sonl=query(u<<1,l,r);
// sonr=query(u<<1|1,l,r);
// pushup(res,sonl,sonr);
// }
// return res;
// // 如果要这样写
// // if(l<=mid) sonl=query(u<<1,l,r);
// // if(r>mid) sonr=query(u<<1|1,l,r);
// // pushup(res,sonl,sonr);
// // return res;
// // 这样写显然是错误的,因为有可能sonl或者sonr为空
// }
void modify(int u,int k,int c){
if(tr[u].l==k&&tr[u].r==k){
tr[u]={k,k,c,c,c,c};
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(k<=mid){
modify(u<<1,k,c);
}
else {
modify(u<<1|1,k,c);
}
pushup(u);
}
int main(){
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i];
build(1,1,n);
while(m--){
int k,x,y;
cin >> k >> x >> y;
if(k==1){
if(x>y) swap(x,y);
cout<<query(1,x,y).trm<<endl;
}
else {
modify(1,x,y);
}
}
return 0;
}
区间最大公约数
//利用线段树维护数组a的差分数组 利用已知数学结论 gcd(a,b,c)=gcd(a,b-c,c-b);
//则区间(l,r)的最大公约数为 gcd(a[l],a[l+1],a[l+2]....a[r])==
//gcd(a[l],a[l+1]-a[l],a[l+2]-a[l+1]...a[r]-a[r-1])=gcd(g[l],gcd(b[l+1],b[l+2]..b[r]))
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
struct Node{
int l,r,sum,g; // 左边界 有边界 区间和 区间最大公约数
}tr[N*4];
int n,m;
int a[N];
void pushup(Node &u,Node &sonl,Node &sonr){
u.sum=sonl.sum+sonr.sum;
u.g=__gcd(sonr.g,sonl.g);
}
void pushup(int u){
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
int tmp=a[r]-a[r-1];
tr[u]={l,r,tmp,tmp};
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int x,int k){
if(tr[u].l==x&&tr[u].r==x){
int tmp=tr[u].sum+k;
tr[u]={x,x,tmp,tmp};
return;
}
int mid=tr[u].l+tr[u].r >> 1;
if(x<=mid) modify(u<<1,x,k);
else {
modify(u<<1|1,x,k);
}
pushup(u);
}
Node query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u];
}
int mid=tr[u].r+tr[u].l >> 1;
Node res;
if(r<=mid) res=query(u<<1,l,r);
else if(l>mid) res=query(u<<1|1,l,r);
else {
Node sonl=query(u<<1,l,r);
Node sonr=query(u<<1|1,l,r);
pushup(res,sonl,sonr);
}
return res;
}
signed main(){
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i];
build(1,1,n);
while(m--){
char choice;
cin >> choice;
if(choice=='Q'){// 询问区间x->y的最大公约数
int x,y;
cin >> x >> y;
int l=query(1,1,x).sum;
int r=0;
if(x+1<=y) {
r=query(1,x+1,y).g;}
int ans=__gcd(l,r);
cout << abs(ans) <<endl;
}
else{
int l,r,k; //将区间(l,r)加上k
cin >> l >> r >> k;
modify(1,l,k);
if(r+1<=n) modify(1,r+1,-k);
}
}
return 0;
}
下面是带懒标记,懒标记可以支持区间修改,不带懒标记只能进行单点修改
一个简单的整数问题2
//线段树 如果不用懒标记 只支持做单点修改
#include<iostream>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
struct Node{
int l,r,sum,add;
}tr[N*4];
void pushup(int u){
tr[u].sum=tr[u << 1].sum+tr[u <<1 | 1 ].sum; //一定要搞清楚实际含义,着他们
//这里多了一个等号 调了一万年
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
tr[u]={l,r,a[l],0};
return;
}
else {
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void pushdown(int u){
auto &root=tr[u],&l=tr[u<<1],&r=tr[u<<1|1];
l.sum+=(l.r-l.l+1)*root.add,l.add+=root.add;
r.sum+=(r.r-r.l+1)*root.add,r.add+=root.add;
root.add=0;
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].sum;
}
int tmp=0;
int mid=tr[u].l+tr[u].r>>1;
//查询区间含不住当前区间
pushdown(u); // 下放!
if(l<=mid) tmp+=query(u<<1,l,r);
if(r>mid) tmp+=query(u<<1|1,l,r);
return tmp;
}
void modify(int u,int l,int r,int k){
if(tr[u].l>=l&&tr[u].r<=r){ // 修改区间含的住当前区间,当前区间值都要改捏
tr[u].sum+=(tr[u].r-tr[u].l+1)*k;//因为我们做的懒标记是不包括根节点的,所以这里应该先给
//根节点加上 ,然后再操作该节点的懒标记
tr[u].add+=k;
return;
}
//含不住 下放,因为modify里面有个pushup操作,如果懒标记没有下传就会导致子节点的值没有第一时间更新,父节点就会被错误的更新
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
signed main(){
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> a[i];
build(1,1,n);
while(m--){
char c;
cin >> c;
if(c=='Q'){//询问区间和
int l,r;
cin >> l >> r;
cout<< query(1,l,r) << endl;
}
else { // 区间l->r 加上c
int l,r,c;
cin >> l >> r >> c;
modify(1,l,r,c);
}
}
return 0;
}
维护序列
#include<iostream>
#define int long long
using namespace std;
const int N=1e5+10;
struct Node {
int l,r,sum,mul,add; // 左边界 有边界 区间和 乘懒标记 加懒标记
}tr[N*4];
int a[N];
int n,m,p;
void excute(Node &u,int mul,int add){ //表示当前对tr[u]执行乘mul加add操作
u.sum=(u.sum*mul+add*(u.r-u.l+1))%p;//我们的懒标记只是存的子节点的操作,对于本层节点直接执行
u.mul=(u.mul*mul)%p;//给节点的乘懒标记进行乘法
u.add=(u.add*mul+add)%p;//给节点的加懒标记进行乘法 并 进行加法
}
void pushdown(int u){
excute(tr[u<<1],tr[u].mul,tr[u].add);
excute(tr[u<<1|1],tr[u].mul,tr[u].add);
tr[u].mul=1,tr[u].add=0;
}
void pushup(int u){
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
tr[u]={l,r,a[l],1,0};
}
else {
tr[u] = {l, r, 0, 1, 0};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int mul,int add){
if(tr[u].l>=l&&tr[u].r<=r){
excute(tr[u],mul,add);
}
else {
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,mul,add);
if(r>mid) modify(u<<1|1,l,r,mul,add);
pushup(u);
}
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].sum;
}
else {
pushdown(u);
int ans=0;
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) ans=query(u<<1,l,r);
if(r>mid) ans=(ans+query(u<<1|1,l,r))%p;
return ans;
}
}
signed main(){
cin >> n >> p;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
cin >> m;
while(m--){
int c,l,r;
cin >> c >> l >> r;
if(c==1){ // 区间乘
int k;
cin >> k;
modify(1,l,r,k,0);
}
else if(c==2){ // 区间加
int k;
cin >> k;
modify(1,l,r,1,k);
}
else{ //区间和
cout << query(1,l,r) << endl;
}
}
return 0;
}
亚特兰蒂斯
//扫描线 且不同pushdown
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct segment{
double x,y1,y2;
int k;
}sem[N*2];
struct Node{
int l,r,cnt; // 左边界 有边界,当前节点被覆盖的次数 ,当前节点被覆盖的长度
double len;
}tr[N*8];
int n,m;
double lsh[N];
bool cmp(segment a,segment b){
return a.x<b.x;
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
return;
}
else {
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
}
void pushup(int u){ // 重点理解 ,由子节点更新父节点
if(tr[u].cnt>0) tr[u].len=lsh[tr[u].r+1+1]-lsh[tr[u].l+1];// 如果当前区间已经被覆盖过 直接返回即可 ,因为离散化问题所以存在+1问题
else if(tr[u].l == tr[u].r) tr[u].len = 0;//如果当前是叶子节点,而且没被覆盖过,则它当然为0
else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;//如果当前区间没被覆盖过,且当前区间不是叶子节点,则它的有效覆盖长度为它的两个儿子相加
}
void modify(int u,int l,int r,int k){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].cnt+=k;
pushup(u);//这里由pushup 是因为叶节点的len需要更新
}
else {
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
}
int tmp;int j=0;
int get(double x){
return lower_bound(lsh+1 , lsh+j+1 ,x) - lsh-1;
}
int main(){
int t=0;
while(cin >> n,n){
j=0,tmp=0,m=0;
for(int i=0;i<n;i++){
double x1,x2,y1,y2;
cin >> x1 >> y1 >> x2 >> y2;
sem[m++]={x1,y1,y2,1};
sem[m++]={x2,y1,y2,-1};
lsh[++j]=y1,lsh[++j]=y2;
}
sort(lsh+1,lsh+1+j);
tmp=unique(lsh+1,lsh+j+1)-lsh-1;
sort(sem,sem+m,cmp);
build(1,0,j);
double res=0;
for(int i=0;i<m;i++){
if(i>0) res+=tr[1].len*(sem[i].x-sem[i-1].x);
modify(1,get(sem[i].y1),get(sem[i].y2)-1,sem[i].k);//注意理解区间与点 这里要-1
}
cout << "Test case #" << ++t << endl;
cout <<"Total explored area: ";
printf("%.2lf\n\n",res);
}
return 0;
}
油漆面积
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e4+10;
struct segment{
int x,y1,y2,k;
}sem[N*2];
struct Node{
int l,r,cnt,len; // 左边界 有边界,当前节点被覆盖的次数 ,当前节点被覆盖的长度
}tr[N*4];
int n,m;
bool cmp(segment a,segment b){
return a.x<b.x;
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
return;
}
else {
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
}
void pushup(int u){ // 重点理解 ,由子节点更新父节点
if(tr[u].cnt>0) tr[u].len=tr[u].r-tr[u].l+1;// 如果当前区间已经被覆盖过 直接返回即可
else if(tr[u].l == tr[u].r) tr[u].len = 0;//如果当前是叶子节点,而且没被覆盖过,则它当然为0
else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;//如果当前区间没被覆盖过,且当前区间不是叶子节点,则它的有效覆盖长度为它的两个儿子相加
}
void modify(int u,int l,int r,int k){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].cnt+=k;
pushup(u);
}
else {
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
}
int main(){
cin >> n;
for(int i=0;i<n;i++){
int x1,x2,y1,y2;
cin >> x1 >> y1 >> x2 >> y2;
sem[m++]={x1,y1,y2,1};
sem[m++]={x2,y1,y2,-1};
}
sort(sem,sem+m,cmp);
build(1,0,10000);
int res=0;
for(int i=0;i<m;i++){
if(i>0) res+=tr[1].len*(sem[i].x-sem[i-1].x);
modify(1,sem[i].y1,sem[i].y2-1,sem[i].k);
}
cout << res;
return 0;
}