hdu4747 线段树
来源:互联网 发布:如何举报淘宝卖家 编辑:程序博客网 时间:2024/06/06 01:26
杭州网赛的题目,看数据范围就知道是线段树。
题目让求所有mex的和,首先可以用o(n)的算法(其实是不超过2*n)预处理出从1到i(i从1到n)的串的mex,然后,依次求2到i(i从2到n)。。。。的mex并求和。
mex有一个性质,即mex【i,j】<=mex【i,j+1】,这个性质可以很容易推得,此题便是利用了这个性质来求解。
当求出下标1开头的所有mex后,求下标2开头的序列时,相当于把原来存在于序列中的第一个数字去掉了,此时,如果这个数字在2到n中没有出现过,那么2到n中所有大于这个数字的mex都可以变成这个数字(因为这个数字在之后没有出现过),如果出现过,那么设这个数字在后面第一次出现的位置为j,那么2到j-1部分内没有出现过这个数字,这个范围内所有大于这个数的mex统统可以变成这个数。依次这样求3,4,。。。。。求和便可以。
此外,由于mex最大为n+1,故对序列中所有大于n的数字都可以不求其下一个出现的位置,因为这个数字不会导致更新。
代码:
#include <iostream>#include <cstring>#include <cstdio>using namespace std;#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define delf int m=(l+r)>>1int next[200020];int v[200020];long long int sum[800020]; //求和,注意爆intint me[200020];int rank[800080]; //记录是否有更新int mex[800080]; //记录从当前求的起始位置到i的序列的mex。int p[200020];int pre[200020]; //记录该数字之前是否出现过int n;void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; mex[rt]=mex[rt<<1|1]; //右端点决定序列的mex,右边mex(i~r)大于左边(i~l),}void pushdown(int l,int r,int rt){ if (rank[rt]==-1) return ; delf; sum[rt<<1]=(long long)(m-l+1)*rank[rt]; sum[rt<<1|1]=(long long)(r-m)*rank[rt]; rank[rt<<1]=rank[rt<<1|1]=rank[rt]; mex[rt<<1]=mex[rt<<1|1]=rank[rt]; rank[rt]=-1; return ;}void build(int l,int r,int rt){ rank[rt]=-1; if (l==r) { sum[rt]=me[l]; mex[rt]=me[l]; return ; } delf; build(lson); build(rson); pushup(rt); //cout<<"AAA "<<l<<" "<<r<<" "<<sum[rt]<<" "<<sum[rt<<1]<<" "<<sum[rt<<1|1]<<endl;}void update(int L,int R,int l,int r,int rt,int v){ if (L<=l&&r<=R) { //cout<<l<<" "<<r<<" "<<v<<endl; sum[rt]=(long long)v*(r-l+1); rank[rt]=mex[rt]=v; return ; } pushdown(l,r,rt); delf; if (L<=m) update(L,R,lson,v); if (R>m) update(L,R,rson,v); pushup(rt); //cout<<"AAA "<<l<<" "<<r<<" "<<sum[rt]<<" "<<sum[rt<<1]<<" "<<sum[rt<<1|1]<<endl; return ;}long long int query(int L,int R,int l,int r,int rt){ if (L<=l&&r<=R) return sum[rt]; pushdown(l,r,rt); delf; long long int s=0; if (L<=m) s=s+query(L,R,lson); if (R>m) s=s+query(L,R,rson); pushup(rt); return s;}int find(int l,int r,int rt,long long int v) //寻找mex大于v[i]的第一次出现的位置,如果没有返回n+1{ if (mex[rt]<=v) return n+1; if (l==r) return l; pushdown(l,r,rt); delf; if (mex[rt<<1]>v) return find(lson,v); else return find(rson,v);}int main(){ while (scanf("%d",&n)&&n) { int mm=0; memset(sum,0,sizeof(sum)); memset(p,0,sizeof(p)); memset(pre,0,sizeof(pre)); for (int i=1;i<=n;i++) { scanf("%d",&v[i]); if (v[i]<=n) p[v[i]]=1; while (p[mm]) //如果这个数字出现过,mm++ mm++; me[i]=mm; next[i]=n+1; } build(1,n,1); for (int i=1;i<=n;i++) //求next数组 { if (v[i]<=n) { if (pre[v[i]]==0) pre[v[i]]=i; else { int c=pre[v[i]]; next[c]=i; pre[v[i]]=i; } } } long long int ans=0; //cout<<sum[1]<<endl; for (int i=1;i<=n;i++) { ans=ans+query(i,n,1,n,1); int ll=find(1,n,1,v[i]); //寻找第一次出现的位置 //cout<<ll<<" "<<next[i]-1<<endl; if (ll<next[i]) //如果大于next[i],就无需更新,因为即使当前的数字去掉,后面可能更新仍有一个相同的数字,无法更新 update(ll,next[i]-1,1,n,1,v[i]); //cout<<ans<<" "<<query(i+1,n,1,n,1)<<endl<<endl; } printf("%I64d\n",ans); }}
0 0
- hdu4747 线段树
- hdu4747 mex 线段树
- hdu4747(线段树区间更新)
- hdu4747 Mex (线段树 好题)
- hdu4747 Mex 线段树 (2013网络赛)
- HDU4747 2013 ACM/ICPC Asia Regional Hangzhou Online Mex(线段树)
- hdu4747——Mex
- 【HDU4747】【JZOJ4680】自然数
- [JZOJ4680] 自然数 [HDU4747] Mex
- 线段树?线段树!
- 线段树?线段树!
- 线段_线段树
- 线段_线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- shell 数组
- dialog相关
- BOOST1.55+VS2010
- velocity学习记录之一(velocity基本概念)
- Hibernate对象状态
- hdu4747 线段树
- lua string源码分析总结
- 初学C++
- 章子怡做作 杨幂是恶媳妇 港媒频频妖魔化的内地明星
- java反射机制详解 及 Method.invoke解释
- Lua基础 coroutine —— Lua的多线程编程
- 黑马程序员-学习对象序列化日记
- Codeforces 417D Cunning Gena(状态压缩dp)
- 【C#历程】Upper in CH