主席树——可持久化线段树:

终于开始学这个了,水平不够,时间来凑。
先%一发马学长。

例①: 没错又是这个题目,区间第k小。
K-th Number POJ - 2104:

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1…n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: “What would be the k-th number in a[i…j] segment, if this segment was sorted?”
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2…5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.
Input
The first line of the input file contains n — the size of the array, and m — the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 10 9 by their absolute values — the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output
For each question output the answer to it — the k-th number in sorted a[i…j] segment.
Sample Input
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output
5
6
3
Hint
This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
struct node
{
    int lc,rc;
    int val;
}t[maxn*22];

int root[maxn],a[maxn],b[maxn];
int cnt=0;

int build(int l,int r)
{
    int p=++cnt;
    t[p].val=t[p].lc=t[p].rc=0;
    if(l==r) return p;

    int mid=(l+r)>>1;
    t[p].lc=build(l,mid);
    t[p].rc=build(mid+1,r);
    return p;
}

int change(int now,int pos,int l,int r)
{
    int p=++cnt;
    t[p]=t[now];
    if(l==r)
    {
        t[p].val++;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change(t[now].lc,pos,l,mid);
    else t[p].rc=change(t[now].rc,pos,mid+1,r);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}

int ask(int pl,int pr,int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int pm=t[t[pr].lc].val-t[t[pl].lc].val;
    if(pm>=k) return ask(t[pl].lc,t[pr].lc,l,mid,k);
    else return ask(t[pl].rc,t[pr].rc,mid+1,r,k-pm);
}

int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int x,y,k;
        cnt=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int cm=unique(b+1,b+n+1)-(b+1);

        root[0]=build(1,cm);
        for(int i=1;i<=n;i++)
            root[i]=change(root[i-1],lower_bound(b+1,b+cm+1,a[i])-b,1,cm);

        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&k);
            printf("%d\n",b[ask(root[x-1],root[y],1,cm,k)]);
        }
    }
    return 0;
}

其实我觉得,完全没有必要一开始建立那棵空树。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
struct node
{
    int lc,rc;
    int val;
}t[maxn*22];

int root[maxn],a[maxn],b[maxn];
int cnt=0;

int change(int now,int pos,int l,int r)
{
    int p=++cnt;
    t[p]=t[now];
    if(l==r)
    {
        t[p].val++;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change(t[now].lc,pos,l,mid);
    else t[p].rc=change(t[now].rc,pos,mid+1,r);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}

int ask(int pl,int pr,int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int pm=t[t[pr].lc].val-t[t[pl].lc].val;
    if(pm>=k) return ask(t[pl].lc,t[pr].lc,l,mid,k);
    else return ask(t[pl].rc,t[pr].rc,mid+1,r,k-pm);
}

int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int x,y,k;
        cnt=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int cm=unique(b+1,b+n+1)-(b+1);

        root[0]=0;
        for(int i=1;i<=n;i++)
            root[i]=change(root[i-1],lower_bound(b+1,b+cm+1,a[i])-b,1,cm);

        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&k);
            printf("%d\n",b[ask(root[x-1],root[y],1,cm,k)]);
        }
    }
    return 0;
}

例②:Super Mario HDU - 4417
Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on every integer point i there is a brick on height hi. Now the question is how many bricks in [L, R] Mario can hit if the maximal height he can jump is H.
Input
The first line follows an integer T, the number of test data.
For each test data:
The first line contains two integers n, m (1 <= n <=10^5, 1 <= m <= 10^5), n is the length of the road, m is the number of queries.
Next line contains n integers, the height of each brick, the range is [0, 1000000000].
Next m lines, each line contains three integers L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)
Output
For each case, output "Case X: " (X is the case number starting from 1) followed by m lines, each line contains an integer. The ith integer is the number of bricks Mario can hit for the ith query.
Sample Input
1
10 10
0 5 2 7 5 4 3 8 7 7
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
Sample Output
Case 1:
4
0
0
3
1
2
0
1
5
1

题意:区间小于等于k的个数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
struct node
{
    int lc,rc;
    int val;
}t[maxn*22];

int root[maxn],a[maxn],b[maxn];
int cnt=0;

int change(int now,int pos,int l,int r)
{
    int p=++cnt;
    t[p]=t[now];
    if(l==r)
    {
        t[p].val++;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change(t[now].lc,pos,l,mid);
    else t[p].rc=change(t[now].rc,pos,mid+1,r);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}

int ask(int pl,int pr,int l,int r,int k)
{
    if(l==r) return t[pr].val-t[pl].val;
    int mid=(l+r)>>1;
    int pm=t[t[pr].lc].val-t[t[pl].lc].val;
    if(k<=mid) return ask(t[pl].lc,t[pr].lc,l,mid,k);
    else return pm+ask(t[pl].rc,t[pr].rc,mid+1,r,k);
}

int main(void)
{
    int t,tt=0;
    scanf("%d",&t);
    int n,m;
    while(t--)
    {
        scanf("%d%d",&n,&m);

        int x,y,k;
        cnt=0;

        b[1]=-inf;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i+1]=a[i];
        }
        b[n+2]=inf;

        sort(b+1,b+n+3);
        int cm=unique(b+1,b+n+3)-(b+1);

        root[0]=0;
        for(int i=1;i<=n;i++)
            root[i]=change(root[i-1],lower_bound(b+1,b+cm+1,a[i])-b,1,cm);

        printf("Case %d:\n",++tt);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&k);
            x++,y++;
            printf("%d\n",ask(root[x-1],root[y],1,cm,upper_bound(b+1,b+cm+1,k)-b-1));
        }
    }
    return 0;
}

例③:D-query SPOJ - DQUERY:
Given a sequence of n numbers a1, a2, …, an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, …, aj.

Input
Line 1: n (1 ≤ n ≤ 30000).
Line 2: n numbers a1, a2, …, an (1 ≤ ai ≤ 1e6).
Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).
Output
For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, …, aj in a single line.
Example
Input
5
1 1 2 1 3
3
1 5
2 4
3 5

Output
3
2
3

题意:查询区间有多少个不同的数。
据说是莫队的板子题目,然而我不会莫队。

用位置线段树建立主席树,每棵树维护数列到当前位置每个数出现的最右位置(每棵树只在最右位置+1),查询时,在第 r 棵树上,查询位置 ≥ l 的元素的个数即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=30100;
const int mod=1e9+7;
struct node
{
    int lc,rc;
    int val;
}t[maxn*2*20];

int root[maxn],a[maxn];
int ha[1000100];
int cnt=0;

int change(int now,int pos,int l,int r,int val)
{
    int p=++cnt;
    t[p]=t[now];
    if(l==r)
    {
        t[p].val+=val;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change(t[now].lc,pos,l,mid,val);
    else t[p].rc=change(t[now].rc,pos,mid+1,r,val);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}

int ask(int p,int pos,int l,int r)
{
    if(l>=pos) return t[p].val;
    int mid=(l+r)>>1;
    if(pos<=mid) return ask(t[p].lc,pos,l,mid)+t[t[p].rc].val;
    else return ask(t[p].rc,pos,mid+1,r);
}

int main(void)
{

    int n,m;
    while(scanf("%d",&n)!=EOF)
    {
        int x,y;
        cnt=0;
        memset(ha,0,sizeof(ha));

        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        root[0]=0;
        for(int i=1;i<=n;i++)
        {
            if(!ha[a[i]])
            {
                root[i]=change(root[i-1],i,1,n,1);
                ha[a[i]]=i;
            }
            else
            {
                root[i]=change(root[i-1],ha[a[i]],1,n,-1);
                root[i]=change(root[i],i,1,n,1);
                ha[a[i]]=i;
            }
        }

        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",ask(root[y],x,1,n));
        }
    }
    return 0;
}

例④:树上路径点权第k小:
Count on a tree SPOJ - COT:
You are given a tree with N nodes. The tree nodes are numbered from 1 to N. Each node has an integer weight.

We will ask you to perform the following operation:

u v k : ask for the kth minimum weight on the path from node u to node v
Input
In the first line there are two integers N and M. (N, M <= 100000)

In the second line there are N integers. The ith integer denotes the weight of the ith node.

In the next N-1 lines, each line contains two integers u v, which describes an edge (u, v).

In the next M lines, each line contains three integers u v k, which means an operation asking for the kth minimum weight on the path from node u to node v.

Output
For each operation, print its result.

Example
Input:
8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
2 5 2
2 5 3
2 5 4
7 8 2
Output:
2
8
9
105
7

就是把主席树搬到树上了,因为主席树的每棵树 root(i),都维护了序列的前缀(1----i)的信息。
所以现在的每棵root(i) 变成维护根节点到 i 的信息就好啦,这样加上个 lca 就愉快的解决啦。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
struct node
{
    int lc,rc;
    int val;
}t[maxn*22];

int root[maxn],a[maxn],b[maxn];
int head[maxn],ver[maxn*2],nt[maxn*2];
int f[maxn][20],d[maxn];
int tot=1,cnt=0,cm,tt;

void add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}

int change(int now,int pos,int l,int r)
{
    int p=++cnt;
    t[p]=t[now];
    if(l==r)
    {
        t[p].val++;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change(t[now].lc,pos,l,mid);
    else t[p].rc=change(t[now].rc,pos,mid+1,r);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}


int ask(int fafa,int fa,int x,int y,int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int pm=t[t[x].lc].val+t[t[y].lc].val-t[t[fa].lc].val-t[t[fafa].lc].val;
    if(pm>=k) return ask(t[fafa].lc,t[fa].lc,t[x].lc,t[y].lc,l,mid,k);
    else return ask(t[fafa].rc,t[fa].rc,t[x].rc,t[y].rc,mid+1,r,k-pm);
}

void bfs(void)
{
    queue<int>q;
    memset(d,0,sizeof(d));
    d[1]=1;
    q.push(1);
    while(q.size())
    {
        int x=q.front();
        q.pop();

        root[x]=change(root[f[x][0]],a[x],1,cm);

        for(int i=head[x];i;i=nt[i])
        {
            int y=ver[i];
            if(d[y]) continue;
            d[y]=d[x]+1;
            f[y][0]=x;
            for(int j=1;j<=tt;j++)
                f[y][j]=f[f[y][j-1]][j-1];
            q.push(y);
        }
    }
}

int lca(int x,int y)
{
    if(d[x]>d[y]) swap(x,y);
    for(int i=tt;i>=0;i--)
        if(d[f[y][i]]>=d[x]) y=f[y][i];

    if(x==y) return x;

    for(int i=tt;i>=0;i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}


int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head,0,sizeof(head));
        tot=1,cnt=0;
        root[0]=0;
        tt=log(n)/log(2)+1;

        int x,y,k;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+n+1);
        cm=unique(b+1,b+n+1)-(b+1);
        for(int i=1;i<=n;i++)
            a[i]=lower_bound(b+1,b+cm+1,a[i])-b;

        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }

        bfs();

        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&k);
            int fa=lca(x,y);
            printf("%d\n",b[ask(root[f[fa][0]],root[fa],root[x],root[y],1,cm,k)]);
        }
    }
    return 0;
}

例⑤:动态区间第k小。
这个东西不怎么好理解。

分别维护原序列主席树(静态第k小)和修改序列主席树。
修改序列一个主席树并不维护一棵前缀权值线段树,(1----i 中 值域 【L,R】中每个数出现的次数)。
而是维护[i−lowbit(i)+1,i]这一段的权值线段树,( i−lowbit(i)+1----i 中 值域【L,R】中每个数出现的次数)。

空间复杂度达到了m* logn * logn 级别的。
不超内存的话就多开一点。。一般200倍左右就够用了。

ZOJ 2112 Dynamic Rankings:
Time Limit: 10000 msMemory Limit: 32768 KB
The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], …, a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], …, a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

  • Reads N numbers from the input (1 <= N <= 50,000)

  • Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], …, a[j] and change some a[i] to t.

Input

The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], …, a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There’re NO breakline between two continuous test cases.

Output

For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],…, a[j])

There’re NO breakline between two continuous test cases.

Sample Input

2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6
3
6

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
int n,m,cnt;
int op[maxn],xx[maxn],yy[maxn],kk[maxn];
int a[maxn],b[maxn<<1];
int root[maxn],root1[maxn],llc[maxn],rrc[maxn],lcnt,rcnt,cm;
struct node
{
    int lc,rc,val;
}t[maxn*400];

void locate(int l,int r)
{
    lcnt=0,rcnt=0;
    for(;l;l-=(l&-l)) llc[++lcnt]=root1[l];
    for(;r;r-=(r&-r)) rrc[++rcnt]=root1[r];
}

int change_in(int now,int pos,int l,int r,int val)
{
    int p=++cnt;
    t[p]=t[now];
    if(l==r)
    {
        t[p].val+=val;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change_in(t[now].lc,pos,l,mid,val);
    else t[p].rc=change_in(t[now].rc,pos,mid+1,r,val);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}

void change_out(int x,int &pos1,int pos2)
{

    for(;x<=n;x+=(x&-x))
    {
        root1[x]=change_in(root1[x],pos1,1,cm,-1);
        root1[x]=change_in(root1[x],pos2,1,cm,1);
    }
    pos1=pos2;

}


int ask(int pl,int pr,int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;

    int ans=0;
    for(int i=1;i<=lcnt;i++) ans-=t[t[llc[i]].lc].val;
    for(int i=1;i<=rcnt;i++) ans+=t[t[rrc[i]].lc].val;
    ans+=t[t[pr].lc].val-t[t[pl].lc].val;

    if(ans>=k)
    {
        for(int i=1;i<=lcnt;i++) llc[i]=t[llc[i]].lc;
        for(int i=1;i<=rcnt;i++) rrc[i]=t[rrc[i]].lc;
        return ask(t[pl].lc,t[pr].lc,l,mid,k);
    }
    else
    {
        for(int i=1;i<=lcnt;i++) llc[i]=t[llc[i]].rc;
        for(int i=1;i<=rcnt;i++) rrc[i]=t[rrc[i]].rc;
        return ask(t[pl].rc,t[pr].rc,mid+1,r,k-ans);
    }

}

int main(void)
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        cnt=0;
        cm=0;
        memset(root1,0,sizeof(root1));

        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[++cm]=a[i];
        }

        char pos[10];
        for(int i=1;i<=m;i++)
        {
            scanf("%s",pos);
            if(pos[0]=='Q')
            {
                op[i]=1;
                scanf("%d%d%d",&xx[i],&yy[i],&kk[i]);
            }
            else
            {
                op[i]=0;
                scanf("%d%d",&xx[i],&yy[i]);
                b[++cm]=yy[i];
            }
        }


        sort(b+1,b+cm+1);
        cm=unique(b+1,b+cm+1)-(b+1);
        for(int i=1;i<=n;i++)
            a[i]=lower_bound(b+1,b+cm+1,a[i])-b;
        for(int i=1;i<=m;i++)
            if(op[i]==0) yy[i]=lower_bound(b+1,b+cm+1,yy[i])-b;

        for(int i=1;i<=n;i++)
            root[i]=change_in(root[i-1],a[i],1,cm,1);



        for(int i=1;i<=m;i++)
        {

            if(op[i])
            {
                locate(xx[i]-1,yy[i]);
                printf("%d\n",b[ask(root[xx[i]-1],root[yy[i]],1,cm,kk[i])]);
            }
            else
            {
                change_out(xx[i],a[xx[i]],yy[i]);
            }
        }

    }
    return 0;
}

例⑥:标记永久化:其实主席树里面区间修改应用并不多。

To the moon HDU - 4348:

Background
To The Moon is a independent game released in November 2011, it is a role-playing adventure game powered by RPG Maker.
The premise of To The Moon is based around a technology that allows us to permanently reconstruct the memory on dying man. In this problem, we’ll give you a chance, to implement the logic behind the scene.

You‘ve been given N integers A [1], A [2],…, A [N]. On these integers, you need to implement the following operations:
1。C l r d: Adding a constant d for every {A i | l <= i <= r}, and increase the time stamp by 1, this is the only operation that will cause the time stamp increase.
2。Q l r: Querying the current sum of {A i | l <= i <= r}.
3。H l r t: Querying a history sum of {A i | l <= i <= r} in time t.
4。 B t: Back to time t. And once you decide return to a past, you can never be access to a forward edition anymore.
… N, M ≤ 10 5, |A [i]| ≤ 10 9, 1 ≤ l ≤ r ≤ N, |d| ≤ 10 4 … the system start from time 0, and the first modification is in time 1, t ≥ 0, and won’t introduce you to a future state.
Input
n m
A 1 A 2 … A n
… (here following the m operations. )
Output
… (for each query, simply print the result. )
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

2 4
0 0
C 1 1 1
C 2 2 -1
Q 1 2
H 1 2 1
Sample Output
4
55
9
15

0
1
题意:有一个由n个数组成的序列,有4中操作:

1.C l r d [l,r]这段区间都加上d

2.Q l r 询问[l,r]这段区间的和

3.H l r t 询问之前t时间[l,r]的区间和

4.B t 回到t时间,且下一秒的时间从t开始

思路:有两种,一种是在线写法,即用主席树写,按时间建立主席树,主席树上的每一棵线段树维护[1,n]这段序列的信息,这里成段更新的时候要注意,以往写线段树的时候,都是把lazy标记向下传,但是写主席树的时候每一次下传,那么新的节点数就会非常多,会爆内存,所以我们不把lazy操作下传,只是在询问的时候,最后累加的答案加上每一个父亲节点上的lazy值。

还有一种是离线写法,我们先把要询问历史区间和的问题都保存下来,并标记下这个问题是在第几次操作后被问的,这里"C l r d","B t"都算是一次操作,然后再记录每次操作中所问的历史区间和的时间。那么我们每一次操作,就先把这个时间可能被后面问到的历史区间和的问题都回答完,用nas[id]记录id这个问题的答案,然后我们把这个操作中问的问题都删除,因为之后这些问题都不会问了。

为了练习以下主席树,就用主席树写法啦。
一个小地方卡了我好久,当前区间更新的时候不只有左子树和右子树能更新当前区间,还有当前区间的laz标记。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100010;
const int mod=1e9+7;
struct node
{
    int lc,rc;
    ll val,laz;
}t[maxn*40];
int root[maxn],cnt;

int build(int l,int r)
{
    int p=++cnt;
    t[p].val=0;
    t[p].laz=0;
    if(l==r)
    {
        scanf("%lld",&t[p].val);
        return p;
    }

    int mid=(l+r)>>1;
    t[p].lc=build(l,mid);
    t[p].rc=build(mid+1,r);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val;
    return p;
}

int change(int now,int cl,int cr,int val,int l,int r)
{
    int p=++cnt;
    t[p]=t[now];
    //t[p].val+=(ll)val*(min(cr,r)-max(cl,l)+1);
    if(cl<=l&&r<=cr)
    {
        t[p].laz+=(ll)val;
        t[p].val+=(ll)val*(r-l+1);
        return p;
    }

    int mid=(l+r)>>1;
    if(cl<=mid) t[p].lc=change(t[now].lc,cl,cr,val,l,mid);
    if(cr>mid) t[p].rc=change(t[now].rc,cl,cr,val,mid+1,r);
    t[p].val=t[t[p].lc].val+t[t[p].rc].val+t[p].laz*(r-l+1);
    return p;
}

ll ask(int now,int cl,int cr,int l,int r)
{
    if(cl<=l&&r<=cr)
    {
        return t[now].val;
    }

    int mid=(l+r)>>1;
    ll ans=t[now].laz*(min(cr,r)-max(cl,l)+1);
    if(cl<=mid) ans+=ask(t[now].lc,cl,cr,l,mid);
    if(cr>mid) ans+=ask(t[now].rc,cl,cr,mid+1,r);

    return ans;
}


int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int now=0;
        cnt=0;
        root[now]=build(1,n);

        char pos[20];
        int x,y,val;
        for(int i=1;i<=m;i++)
        {
            scanf("%s",pos);
            if(pos[0]=='C')
            {
                scanf("%d%d%d",&x,&y,&val);
                now++;
                root[now]=change(root[now-1],x,y,val,1,n);
            }
            else if(pos[0]=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%lld\n",ask(root[now],x,y,1,n));
            }
            else if(pos[0]=='H')
            {
                scanf("%d%d%d",&x,&y,&val);
                printf("%lld\n",ask(root[val],x,y,1,n));
            }
            else
            {
                scanf("%d",&val);
                now=val;
            }
        }

    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值