bzoj 5076: [Lydsy十月月赛]小B的咒语

来源:互联网 发布:金蝶k3软件多少钱 编辑:程序博客网 时间:2024/05/21 14:55

题意

小 B 成为了一个魔法师!他得到了一串由正整数组成的咒语 a。经过他的研究,某一个咒语的威力可以表示为,每个在此咒语中出现过的数字,在这个咒语中最后一次出现与第一次出现的位置之差。小 B 做出了若干次操作,每次操作都有可能是计算咒语的某个子串的威力。同时,小 B 也在不断地实验中,所以操作也有可能是把咒语中某个位置上的数字换成另一个。可是他太小,不会算,请你帮帮他。

题解

这题求出最右端点减去最短端点
不难想到,这个的话对于每一个点,记录上一个是谁
然后把全部加起来
然后一开始是这么想的。。
然后打算用线段树维护。
然后想到一个问题,就是最左端的不可以加
然后想了很久都没想到。。
于是看了下题解CDQ。。
然后才想起来。。
用一个CDQ然后要他的上一个也列入考虑范围了
其实就是把一个点的左边看做(x,pre),权值自然就是(x-pre)
然后你每一次就是看做询问(l,l)到(r,r)这个矩阵里面的和
然后就可以了。。
然后在我不用归并之前,一直T。。改了才过的QAQ

教训

像这种有多个限制的,一定要记得类似CDQ的解法

CODE:

#include<cstdio>#include<algorithm>#include<iostream>#include<cstring>#include<set>using namespace std;typedef long long LL;const int N=100005*10;int n,q;struct qq{    LL x,y;//坐标     int id;     int op;//是什么操作     0:加点     1:删点     2:正的询问   3:负的询问}s[N];int tot;LL ans[N];bool ok[N];//这个地方是不是答案set<int> o[N];int next[N],last[N];int k[N];bool cmp (qq x,qq y){return x.x<y.x;}LL f[N];int lb (int x){return x&(-x);}void change (int x,LL y){    while (x<N)    {        f[x]+=y;        x+=lb(x);    }}void reset (int x){    while (x<=100000)    {        f[x]=0;        x+=lb(x);    }}LL find (int x){    LL lalal=0;    while (x>=1)    {        lalal=lalal+f[x];        x-=lb(x);    }    return lalal;}qq h[N];void dfs (int l,int r){    if (l==r) return ;    int mid=(l+r)>>1;    dfs(l,mid);dfs(mid+1,r);/*  printf("%d %d\n",l,r);    for (int u=l;u<=mid;u++)        printf("%lld %lld\n",s[u].x,s[u].y);    printf("\n");    for (int u=mid+1;u<=r;u++)    printf("%lld %lld\n",s[u].x,s[u].y);    system("pause");*/    //sort(s+l,s+1+mid,cmp);sort(s+1+mid,s+1+r,cmp);    int now=l;    for (int u=mid+1;u<=r;u++)    {        while (now<=mid)        {            if (s[now].op==2||s[now].op==3)            {                now++;                continue;            }            if (s[now].x<=s[u].x)            {                if (s[now].op==0)   change(s[now].y,s[now].x-s[now].y);                else change(s[now].y,-(s[now].x-s[now].y));                now++;            }            else break;        }        if (s[u].op==2)         {            ans[s[u].id]+=find(s[u].y);            //printf("add:%d %d %d\n",s[u].id,find(s[u].y),s[u].y);        }        if (s[u].op==3)         {            ans[s[u].id]-=find(s[u].y);        //  printf("del:%d %d\n",s[u].id,find(s[u].y));        }    }    for (int u=l;u<=now;u++)        if (s[u].op==0||s[u].op==1)            reset(s[u].y);    int i=l,j=mid+1;    int shen=0;    while (i<=mid&&j<=r)    {        if (s[i].x<s[j].x) h[++shen]=s[i++];        else h[++shen]=s[j++];    }    while (i<=mid) h[++shen]=s[i++];    while (j<=r) h[++shen]=s[j++];    for (int u=l;u<=r;u++) s[u]=h[u-l+1];    return ;}int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();    return x*f;}set<int>::iterator it;int a[N];int Last[N];//这个值的最后一个是什么int main(){    tot=0;    n=read();q=read();    for (int u=1;u<=n;u++)       {        int x=read();        a[u]=x;        last[u]=k[x];        next[k[x]]=u;        k[x]=u;        Last[x]=u;        o[x].insert(u);    }    for (int u=1;u<=n;u++)        if (last[u]!=0)            s[++tot]=(qq){u,last[u],0,0};    for (int u=1;u<=q;u++)    {        char ss[5];        scanf("%s",ss);        if (ss[0]=='Q')//询问操作        {            ok[u]=true;            int l,r;            l=read();r=read();            s[++tot]=(qq){r,r,u,2};            s[++tot]=(qq){l-1,r,u,3};            s[++tot]=(qq){r,l-1,u,3};            s[++tot]=(qq){l-1,l-1,u,2};        }        else        {            ok[u]=false;            int x,y;            x=read();y=read();            //x这个地方变为y            if (Last[a[x]]==x) Last[a[x]]=last[x];//换人了             if (last[x]!=0) s[++tot]=(qq){x,last[x],u,1};            if (next[x]!=0) s[++tot]=(qq){next[x],x,u,1};            if (last[x]!=0&&next[x]!=0)//两边都有,家人                 s[++tot]=(qq){next[x],last[x],u,0};            next[last[x]]=next[x];            last[next[x]]=last[x];            o[a[x]].erase(x);            //加入新的点            a[x]=y;            it=o[y].lower_bound(x);//找到这个值            if (it==o[y].end())//如果没有,那么它就是最后一个了            {                if (Last[y]!=0)                 {                    s[++tot]=(qq){x,Last[y],u,0};                    next[Last[y]]=x;                    last[x]=Last[y];                }                else last[x]=0;                Last[y]=x;                next[x]=0;            }            //0:加点     1:删点            else            {                int now=(*it);//找到位置                if (last[now]!=0)//如果它还有上一个                 {                    s[++tot]=(qq){now,last[now],u,1};                    s[++tot]=(qq){x,last[now],u,0};                    s[++tot]=(qq){now,x,u,0};                    next[last[now]]=x;                    last[x]=last[now];                    last[now]=x;                    next[x]=now;                }                else                {                    s[++tot]=(qq){now,x,u,0};                    next[x]=now;                    last[now]=x;                    last[x]=0;                }            }            o[y].insert(x);        }    }    dfs(1,tot);    for (int u=1;u<=q;u++)        if (ok[u])            printf("%lld\n",ans[u]);    return 0;}