CodeForces 848C Goodbye Souvenir(CDQ分治+平衡树/set+树状数组)

来源:互联网 发布:python爬虫书籍pdf 编辑:程序博客网 时间:2024/05/22 08:29

C. Goodbye Souvenir
time limit per test:6 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

I won't feel lonely, nor will I be sorrowful... not before everything is buried.

A string of n beads is left as the message of leaving. The beads are numbered from1 to n from left to right, each having a shape numbered by integers between1 and n inclusive. Some beads may have the same shapes.

The memory of a shape x in a certain subsegment of beads, is defined to be the difference between the last position and the first position that shapex appears in the segment. The memory of a subsegment is the sum of memories over all shapes that occur in it.

From time to time, shapes of beads change as well as the memories. Sometimes, the past secreted in subsegments are being recalled, and you are to find thememory for each of them.

Input

The first line of input contains two space-separated integers n and m (1 ≤ n, m ≤ 100 000) — the number of beads in the string, and the total number of changes and queries, respectively.

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ n) — the initial shapes of beads1, 2, ..., n, respectively.

The following m lines each describes either a change in the beads or a query of subsegment. A line has one of the following formats:

  • 1 p x (1 ≤ p ≤ n,1 ≤ x ≤ n), meaning that the shape of thep-th bead is changed into x;
  • 2 l r (1 ≤ l ≤ r ≤ n), denoting a query ofmemory of the subsegment from l to r, inclusive.
Output

For each query, print one line with an integer — the memory of the recalled subsegment.

Examples
Input
7 61 2 3 1 3 2 12 3 72 1 31 7 21 3 22 1 62 5 7
Output
5071
Input
7 51 3 2 1 4 2 31 1 42 2 31 1 72 4 51 1 7
Output
00
Note

The initial string of beads has shapes (1, 2, 3, 1, 3, 2, 1).

Consider the changes and queries in their order:

  1. 2 3 7: the memory of the subsegment [3, 7] is (7 - 4) + (6 - 6) + (5 - 3) = 5;
  2. 2 1 3: the memory of the subsegment [1, 3] is (1 - 1) + (2 - 2) + (3 - 3) = 0;
  3. 1 7 2: the shape of the 7-th bead changes into 2. Beads now have shapes(1, 2, 3, 1, 3, 2, 2) respectively;
  4. 1 3 2: the shape of the 3-rd bead changes into 2. Beads now have shapes(1, 2, 2, 1, 3, 2, 2) respectively;
  5. 2 1 6: the memory of the subsegment [1, 6] is (4 - 1) + (6 - 2) + (5 - 5) = 7;
  6. 2 5 7: the memory of the subsegment [5, 7] is (7 - 6) + (5 - 5) = 1.


        再度加深对CDQ分治的理解。大致题意:给你一个数字序列,然后有两种操作。一是修改,把某个位置的数字改成另外一个数字;二是求一个区间[l,r]中所有同类数字的最大跨度和,即如样例所示。

        首先,对于这个同类数字中的最大跨度和,对于每一个数字可以按照套路,分成所有前后相邻两个同类数字的距离和。即1、2、1、3、1、2、3对于数字1的最大跨度,我们可以写成(3-1)+(5-3)=4,也样子相当于只需要维护前后两个相邻的数字即可。然后,我们考虑如何对于每个数字维护这个前后关系,支持删除然后又支持修改,显然平衡树再合适不过。对于每一个数字,我建立一颗平衡树,然后维护前驱和后继,添加和删除。最后对于区间的查询,我们在平衡树外面套一个主席树即可完成区间的求值,对于每一个区间内的数字位置求和即可。

        然而,本题的目的并不是考数据结构,真正用树套树的话内存也不够。所以说这里得考虑用CDQ分治大法,对于平衡树,由于只是利用到前驱和后继以及删除,所以STL的set也足够用了。对于一个数字,能够把它与它的前驱的位置差值加入到结果中的条件是它们两个都在所求区间中。根据这一点,我们考虑把这两个东西作为坐标,在二维的平面上操作,对于时间分治。询问[l,r]之间的和也就可以对应到一个矩形的区间中,范围是(l,l)~(r,r)。对于每一个点,我们有三个参数(x,y,val),x表示当前位置,y表示前一个同类数字的位置,val表示两个位置的差值,x、y同时表示在平面中的位置。这样就有了时间和两个平面坐标这三个参数,可以转换为一个三维偏序的问题。具体做法与传统的三维偏序问题类似。对于修改操作则是采用先删除再添加的方式,注意对于ans要使用LL。具体见代码:

#include<bits/stdc++.h>#define lowbit(x) x&-x#define INF 0x3f3f3f3f#define LL long long#define N 100010using namespace std;struct BinaryIndexedTree{LL c[N];inline void update(int x,int k){while (x<N){c[x]+=k;x+=lowbit(x);}}inline LL getsum(int x){LL ans=0;while (x>0){ans+=c[x];x-=lowbit(x);}return ans;}void clear(int x){while (x<N){c[x]=0;x+=lowbit(x);}}} BIT;struct node{    int tp,x,y,val;    bool operator < (node a) const    {        return x==a.x?tp<a.tp:x<a.x;    }} q[N<<3],tmp[N<<3];int n,m,tot,num,pre[N];set<int> st[N];LL ans[N];void update(int x,int y)//在set中修改前驱和后继{    set<int>:: iterator it,pos;    if (pre[x]==y) return;    if (pre[x]!=0)//如果之前位置是别的数字,那么先删除    {        pos=it=st[pre[x]].find(x);        if (it!=st[pre[x]].begin())//有前驱那么删除该点与前驱的关系        {            it--;            q[++tot]=node{0,x,*it,*it-x};            it++;        }        pos++;        if (pos!=st[pre[x]].end())//有后继那么删除该点与后继的关系        {            q[++tot]=node{0,*pos,x,x-*pos};            if (it!=st[pre[x]].begin())            {                it--;                q[++tot]=node{0,*pos,*it,*pos-*it};//如果前驱后继都有,那么回复前驱与后继的关系                it++;            }        }        st[pre[x]].erase(it);//在set中删除该点    }    pos=it=st[y].insert(x).first;//添加新的点,处理过程大致与删除类似,不一一赘述    if (it!=st[y].begin())    {        it--;        q[++tot]=node{0,x,*it,x-*it};        it++;    }    pos++;    if (pos!=st[y].end())    {        q[++tot]=node{0,*pos,x,*pos-x};        if (it!=st[y].begin())        {            it--;            q[++tot]=node{0,*pos,*it,*it-*pos};            it++;        }    }    pre[x]=y;}void query(int id,int l,int r)//区间询问对应到平面上{    q[++tot]=node{id,r,r,1};    q[++tot]=node{id,r,l-1,-1};    q[++tot]=node{id,l-1,r,-1};    q[++tot]=node{id,l-1,l-1,1};}void cdq(int l,int r)//CDQ分治{    if (r-l<1) return;    int mid=(l+r)>>1;    cdq(l,mid); cdq(mid+1,r);    int p1=l,p2=mid+1,p=l;    while(p1<=mid&&p2<=r)//归并排序的同时用左边更新右边    {        if (q[p1]<q[p2])        {            if (q[p1].tp==0)                BIT.update(q[p1].y,q[p1].val);            tmp[p++]=q[p1++];        } else        {            if (q[p2].tp)                ans[q[p2].tp]+=q[p2].val*BIT.getsum(q[p2].y);            tmp[p++]=q[p2++];        }    }    while(p1<=mid) tmp[p++]=q[p1++];    while(p2<=r)    {        if (q[p2].tp)            ans[q[p2].tp]+=q[p2].val*BIT.getsum(q[p2].y);        tmp[p++]=q[p2++];    }    for(int i=l;i<=r;i++)//删除影响    {        q[i]=tmp[i];        if (q[i].tp==0) BIT.clear(q[i].y);    }}int main(){    num=tot=0;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        int x; scanf("%d",&x);        update(i,x);    }    for(int i=1;i<=m;i++)    {        int op,x,y;        scanf("%d%d%d",&op,&x,&y);        if (op==1) update(x,y); else query(++num,x,y);    }    cdq(1,tot);    for(int i=1;i<=num;i++)        printf("%lld\n",ans[i]);    return 0;}

阅读全文
0 0