poj3468 splay区间修改和查询

线段树的题目,拿来练第一道splay维护区间。

像这种基本的操作修改查询,我现在应该能在20分钟手写好splay再用10分钟调试,基本上不靠模版30分钟应该能出。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define key_val ch[ch[rt][1]][0]
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=100010;
const int INF=1e9+10;

int n,m;
int a[maxn];
int L,R,c;char op[5];

int key[maxn],pre[maxn],sz[maxn],ch[maxn][2],rt,tot1;
int s[maxn],tot2;
int val[maxn];
ll add[maxn],sum[maxn];

void newnode(int &r,int fa,int v)
{
    if(tot2) r=s[tot2--];
    else r=++tot1;
    key[r]=0;
    add[r]=sum[r]=0;
    val[r]=v;
    sz[r]=1;
    MS0(ch[r]);
    pre[r]=fa;
}

void down(int r)
{
    if(add[r]){
        val[r]+=add[r];
        add[ch[r][0]]+=add[r];
        add[ch[r][1]]+=add[r];
        sum[ch[r][0]]+=add[r]*sz[ch[r][0]];
        sum[ch[r][1]]+=add[r]*sz[ch[r][1]];
        add[r]=0;
    }
}

void up(int r)
{
    sz[r]=sz[ch[r][0]]+sz[ch[r][1]]+1;
    sum[r]=sum[ch[r][0]]+sum[ch[r][1]]+val[r];
}

void rot(int x,int kind)
{
    int y=pre[x];
    down(x);down(y);
    ch[y][kind^1]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    up(y);
}

void splay(int x,int goal)
{
    down(x);
    while(pre[x]!=goal){
        if(pre[pre[x]]==goal) rot(x,ch[pre[x]][0]==x);
        else{
            int y=pre[x],z=pre[y];
            int kind=ch[y][0]==x,one=0;
            if(ch[y][0]==x&&ch[z][0]==y) one=1;
            if(ch[y][1]==x&&ch[z][1]==y) one=1;
            if(one) rot(y,kind),rot(x,kind);
            else rot(x,kind),rot(x,kind^1);
        }
    }
    if(goal==0) rt=x;
    up(x);
}

void rto(int k,int goal)
{
    int r=rt;
    down(r);
    while(sz[ch[r][0]]!=k){
        if(k<sz[ch[r][0]]) r=ch[r][0];
        else k-=sz[ch[r][0]]+1,r=ch[r][1];
        down(r);
    }
    splay(r,goal);
}

ll query(int l,int r)
{
    rto(l-1,0);
    rto(r+1,rt);
    return sum[key_val];
}

void update(int l,int r,int c)
{
    rto(l-1,0);
    rto(r+1,rt);
    add[key_val]+=c;
    sum[key_val]+=c*sz[key_val];
}

void build(int &x,int l,int r,int fa)
{
    if(l>r) return;
    int m=(l+r)>>1;
    newnode(x,fa,a[m]);
    build(ch[x][0],l,m-1,x);
    build(ch[x][1],m+1,r,x);
    up(x);
}

void Init()
{
    tot1=rt=0;
    key[0]=pre[0]=sz[0]=ch[0][0]=ch[0][1]=0;
    add[0]=sum[0]=0;
    newnode(rt,0,-INF);
    newnode(ch[rt][1],rt,INF);
    sz[rt]=2;
    build(key_val,1,n,ch[rt][1]);
    up(ch[rt][1]);
    up(rt);
}

int main()
{
    freopen("in.txt","r",stdin);
    while(cin>>n>>m){
        REP(i,1,n) scanf("%d",&a[i]);
        Init();
        while(m--){
            scanf("%s",op);
            if(op[0]==‘Q‘){
                scanf("%d%d",&L,&R);
                printf("%I64d\n",query(L,R));
            }
            else{
                scanf("%d%d%d",&L,&R,&c);
                update(L,R,c);
            }
        }
    }
    return 0;
}

时间: 2024-10-26 07:01:28

poj3468 splay区间修改和查询的相关文章

Wikilo 1191线段树区间修改单点查询

这题也算比较容易的了. 如果哪个区间已经没有黑色的话,就不用update了,就是因为这个原因WA了2发,唉-- #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #incl

【树状数组区间修改单点查询+分组】HDU 4267 A Simple Problem with Integers

http://acm.hdu.edu.cn/showproblem.php?pid=4267 [思路] 树状数组的区间修改:在区间[a, b]内更新+x就在a的位置+x. 然后在b+1的位置-x 树状数组的单点查询:求某点a的值就是求数组中1~a的和. (i-a)%k==0把区间分隔开了,不能直接套用树状数组的区间修改单点查询 这道题的K很小,所以可以枚举k,对于每个k,建立k个树状数组,所以一共建立55棵树 所以就可以多建几棵树..然后就可以转换为成段更新了~~ [AC] 1 #include

洛谷 P3368 【模板】树状数组 2 如题(区间修改+单点查询)

P3368 [模板]树状数组 2 时空限制1s / 128MB 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出

线段树区间修改与查询

单点修改与查询 //单点修改,区间询问最小值 #include <iostream> #include <cstdio> #define maxn 101 #define INF 0x7fffffff using namespace std; int a[maxn],n,m; int mi[maxn]; void build(int k,int l,int r)//k是当前节点编号,l,r为当前节点代表区间 { if(l==r) { mi[k]=a[l]; return; } in

基本线段树模板(建树、点/区间修改、查询)

线段树主要用于区间记录信息(如区间和.最大最小值等),首先是建树: 这里以求和为例: 1 const int MAXM=50000; //定义 MAXM 为线段最大长度 2 3 int a[MAXM+5],segtree[(MAXM<<2)+5]; // a 数组为 main 函数中读入的内容,segtree 数组为需要查询的数的信息(如和.最值等),树的空间大小为线段最大长度的四倍 4 5 void build(int o,int l,int r){ //传入的参数为 o:当前需要建立的结点

POJ 3468 A Simple Problem with Integers(线段树区间修改及查询)

Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval. In

AcWing 242 一个简单整数问题(区间修改 单点查询)

原题 该题涉及树状数组又一串操作: ① 区间修改 运用差分的思想,我们新建了一个数组b,初始化为零,对于每个指令"C l r d",我们只需将其转化为以下操作: 1.把b[l]加上d 2.再把b[r+1]减去d inline void add(int x,int y) { for(;x<=n;x+=x&-x) { c[x]+=y; } } add(l,j); add(r+1,-j); ② 单点查询 执行了以上操作后,b数组的前缀和b[1~x]就代表了该指令对a[x]的影响

树状数组模板(区间修改+单点查询)

很巧妙的用了差分建树,解决区间修改的问题 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=5e5+5; 5 6 int n,m; 7 int a[maxn]; 8 ll tree[maxn]; 9 10 int lowbit(int x){ 11 return x&(-x); 12 } 13 14 void add(int idx,int v){ 15

Luogu P3368 【模板】树状数组 2 [区间修改-单点查询]

P3368 [模板]树状数组 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出第x个数的值 输出格式: 输出