5-14 数据结构啊poi B.mex

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=78124#problem/B

//想看题目的@willinglive

因为mex有单调性,所以我们用线段树来维护以每个点为右端点的mex的和。然后枚举左端点。修改左端点的时候只需要修改当前到下一次该点的值出现的位置-1就可以了

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
int a[200001],v[200001],last[200001],nextx[200001];
long long mex[200001];
int n;
struct num
{
     int x,t;
}b[200001];
inline bool cmp(num x,num y)
{
     if(x.x<y.x)
          return true;
     return false;
}
struct tree
{
     int l,r;
     long long s;
     long long mi,ma;
     long long tag;
}tr[1600001];
inline void up(int p)
{
     tr[p].s=tr[p*2].s+tr[p*2+1].s;
     tr[p].mi=min(tr[p*2].mi,tr[p*2+1].mi);
     tr[p].ma=max(tr[p*2].ma,tr[p*2+1].ma);
}
inline void push(int p)
{
	 if(tr[p].tag==-1)
	      return ;
	 long long len=(tr[p].r-tr[p].l+1);;
     tr[p*2].s=tr[p].tag*(len-len/2);
	 tr[p*2+1].s=tr[p].tag*(len/2);
     tr[p*2].mi=tr[p].tag;
	 tr[p*2+1].mi=tr[p].tag;
     tr[p*2].ma=tr[p].tag;
	 tr[p*2+1].ma=tr[p].tag;
	 tr[p*2].tag=tr[p].tag;
	 tr[p*2+1].tag=tr[p].tag;
     tr[p].tag=-1;
}
inline void build(int p,int l,int r)
{
	 tr[p].l=l;
	 tr[p].r=r;
     if(l!=r)
     {
          int mid=(l+r)/2;
          build(p*2,l,mid);
          build(p*2+1,mid+1,r);
          up(p);
          tr[p].tag=-1;
     }
     else
     {
          tr[p].s=mex[l];
          tr[p].mi=mex[l];
          tr[p].ma=mex[l];
          tr[p].tag=-1;
     }
}
inline void cover(int p,int l,int r,int x)
{
     if((l<=tr[p].l&&tr[p].r<=r&&tr[p].mi>x))
     {
     	  long long len=(tr[p].r-tr[p].l+1);
          tr[p].tag=x;
          tr[p].s=x*len;
          tr[p].mi=x;
          tr[p].ma=x;
     }
     else if(tr[p].ma<x)
          return ;
     else
     {
     	  push(p);
          int mid=(tr[p].l+tr[p].r)/2;
          if(l<=mid)
               cover(p*2,l,r,x);
          if(r>mid)
		       cover(p*2+1,l,r,x);
          up(p);
     }
}
inline void change(int p,int l,int r,int x)
{
     if(l<=tr[p].l&&tr[p].r<=r)
     {
          tr[p].s=x;
          tr[p].mi=x;
          tr[p].ma=x;
     }
     else
     {
     	  push(p);
          int mid=(tr[p].l+tr[p].r)/2;
          if(l<=mid)
               change(p*2,l,r,x);
          if(r>mid)
		       change(p*2+1,l,r,x);
          up(p);
     }
}
inline long long ask(int p,int l,int r)
{
     if(l<=tr[p].l&&tr[p].r<=r)
          return tr[p].s;
     else
     {
     	  push(p);
          int mid=(tr[p].l+tr[p].r)/2;
          long long ans=0;
          if(l<=mid)
               ans+=ask(p*2,l,r);
          if(r>mid)
		       ans+=ask(p*2+1,l,r);
          return ans;
     }
}
inline void changex(int x)
{
     int i;
     int ed=nextx[x];
     if(ed==0)
          ed=n+1;
     mex[x]=0;
     change(1,x,x,mex[x]);
     cover(1,x+1,ed-1,a[x]);
}
int f[200001];
int fx[200001];
int main()
{
//	 freopen("data.in","r",stdin);
//	 freopen("data.out","w",stdout);
     scanf("%d",&n);
     while(n!=0)
     {
          int i,j;
          for(i=1;i<=n;i++)
          {
               scanf("%d",&a[i]);
               b[i].x=a[i];
               b[i].t=i;
          }
          sort(b+1,b+1+n,cmp);
          int tot=1;
          f[tot]=0;
          fx[0]=tot;
          for(i=1;i<=n;i++)
          {
               if(b[i].x!=f[tot])
               {
                    tot++;
                    f[tot]=b[i].x;
               }
	     	   fx[b[i].t]=tot;
          }
          memset(v,0,sizeof(v));
          int now=1;
          for(i=1;i<=n;i++)
          {
               int d;
			   v[fx[i]]=1;
               while(v[now]!=0&&f[now+1]==f[now]+1)
                    now++;
               if(v[now]!=0&&f[now+1]!=f[now]+1)
                    d=f[now]+1;
               else
                    d=f[now];
               mex[i]=d;
          }
          build(1,1,n);
          memset(last,0,sizeof(last));
          for(i=1;i<=n;i++)
          {
               nextx[last[fx[i]]]=i;
               nextx[i]=n+1;
               last[fx[i]]=i;
          }
          long long ans=0;
          for(i=1;i<=n;i++)
          {
               ans+=ask(1,i,n);
               changex(i);
          }
          printf("%I64d\n",ans);
          scanf("%d",&n);
     }
     return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值