[2016北京集训试题17]数组-[线段树]

Description

[2016北京集训试题17]数组-[线段树]_第1张图片

Solution

线段树乱搞orz。

定义pre[i]为从i点往前找到第1个颜色和点i相同的点。树状数组记录max和sum。max记录区间[l,r]内pre的最大值,sum记录区间[l,r]内的答案总和。注意:最终的答案是取

$n*(n+1)/2-\sum _{r=1}^{n}max(pre[i],1\leq i\leq r)$,即枚举所有子区间的右节点,找最左的左节点。因此,sum[x](记录区间[l,r])在更新的时候,sum[x的左节点]可以直接加上,而对于x的右节点要保证区间[mid+1,r]的数和max[x的左节点]取最大值。这个可以在log(n)内搞定。

然后。。就ok了。

我怕是学了个假的线段树。

Code

#include
#include
#include
#include
#include<set>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m;
int col[N],pre[N];
set<int>id[N];
ll sum[N<<2],mx[N<<2];
ll solve(int k,int l,int r,ll x)
{
    if (l==r) return max(x,mx[k]);
    int mid=(l+r)/2;
    if (mx[k]<=x) return x*(r-l+1);
    if (mx[k<<1]<=x) return 1ll*x*(mid-l+1)+solve(k<<1|1,mid+1,r,x);
    return solve(k<<1,l,mid,x)+sum[k]-sum[k<<1];
}
void pushup(int k,int mid,int r)
{
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
    sum[k]=sum[k<<1]+solve(k<<1|1,mid+1,r,mx[k<<1]);
}
void modify(int k,int l,int r,int x,int y)
{
    if (l==r){sum[k]=mx[k]=y;return;}
    int mid=(l+r)/2;
    if (x<=mid) modify(k<<1,l,mid,x,y);else modify(k<<1|1,mid+1,r,x,y);
    pushup(k,mid,r);
}
void build(int k,int l,int r)
{
    if (l==r){sum[k]=mx[k]=pre[l];return;}
    int mid=(l+r)/2;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    pushup(k,mid,r);
}
set<int>::iterator q,l,r;
int x,to;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) id[i].insert(0);
    for (int i=1;i<=n;i++) 
    {
        scanf("%d",&col[i]);
        pre[i]=*(--id[col[i]].end());
        id[col[i]].insert(i);
    }
    build(1,1,n);
    int Q,opt;
    scanf("%d",&Q);
    while (Q--)
    {
        scanf("%d",&opt);
        if (!opt) printf("%lld\n",1ll*n*(n+1)/2-sum[1]);
        else
        {
            scanf("%d%d",&x,&to);
            q=id[col[x]].find(x);
            l=r=q;l--;r++;
            if (r!=id[col[x]].end())
            {
                pre[*r]=*l;
                modify(1,1,n,*r,*l);
            }
            id[col[x]].erase(q); 
            col[x]=to;id[to].insert(x);
            q=id[to].find(x);
            l=r=q;l--;r++;
            if (r!=id[to].end())
            {
                pre[*r]=x;
                modify(1,1,n,*r,x);
            }
            pre[x]=*l;
            modify(1,1,n,x,*l);
             
        }
    }
}

 

转载于:https://www.cnblogs.com/coco-night/p/9733342.html

你可能感兴趣的:([2016北京集训试题17]数组-[线段树])