HDU 4747 Mex(线段树)
2013-12-04 16:43
246 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4747
题意:给出一个数列A。计算所有的mex(i,j)之和。1<=i<=j<=n。
思路:从前向后依次统计以该位置为左端点的区间之和。那么现在我们考虑i计算之后后面的变化。考虑极端情况,若i位置之后的所有数字都大于i,那么i计算之后后面的所有数对的mex值起码为A[i]。那么,我们记录i之后数字A[i]出现的最早位置next[i],每次用A[i]更新[i+1,next[i]-1]这个区间即可。
struct node
{
int L,R,Max,Min,det;
i64 sum;
void set(int x)
{
det=x;
Max=Min=x;
sum=(i64)(R-L+1)*x;
}
};
node a[N<<2];
int d
,next
,p
,b
;
int n;
void pushUp(int t)
{
if(a[t].L==a[t].R) return;
a[t].sum=a[t*2].sum+a[t*2+1].sum;
a[t].Min=min(a[t*2].Min,a[t*2+1].Min);
a[t].Max=max(a[t*2].Max,a[t*2+1].Max);
}
void pushDown(int t)
{
if(a[t].L==a[t].R) return;
int M=(a[t].L+a[t].R)>>1;
if(a[t].det>0)
{
a[t*2].set(a[t].det);
a[t*2+1].set(a[t].det);
a[t].det=0;
}
}
void build(int t,int L,int R)
{
a[t].L=L;
a[t].R=R;
a[t].det=0;
if(L==R)
{
a[t].Max=a[t].Min=a[t].sum=b[L];
return;
}
int M=(L+R)>>1;
build(t*2,L,M);
build(t*2+1,M+1,R);
pushUp(t);
}
void update(int t,int L,int R,int x)
{
if(L<=a[t].L&&a[t].R<=R&&x<a[t].Min)
{
a[t].set(x);
return;
}
pushDown(t);
int M=(a[t].L+a[t].R)>>1;
if(M>=L&&a[t*2].Max>x) update(t*2,L,R,x);
if(M<R&&a[t*2+1].Max>x) update(t*2+1,L,R,x);
pushUp(t);
}
int main()
{
Rush(n)
{
if(n==0) break;
int i,j=0;
clr(p,0);
FOR1(i,n)
{
RD(d[i]);
if(d[i]>n) d[i]=n+1;
p[d[i]]=1;
while(p[j]) j++;
b[i]=j;
}
for(i=0;i<=n+1;i++) p[i]=n+1;
for(i=n;i>=1;i--)
{
next[i]=p[d[i]];
p[d[i]]=i;
}
build(1,1,n);
i64 ans=a[1].sum;
FOR1(i,n)
{
if(i+1<=next[i]-1) update(1,i+1,next[i]-1,d[i]);
update(1,i,i,0);
ans+=a[1].sum;
}
printf("%I64d\n",ans);
}
}
题意:给出一个数列A。计算所有的mex(i,j)之和。1<=i<=j<=n。
思路:从前向后依次统计以该位置为左端点的区间之和。那么现在我们考虑i计算之后后面的变化。考虑极端情况,若i位置之后的所有数字都大于i,那么i计算之后后面的所有数对的mex值起码为A[i]。那么,我们记录i之后数字A[i]出现的最早位置next[i],每次用A[i]更新[i+1,next[i]-1]这个区间即可。
struct node
{
int L,R,Max,Min,det;
i64 sum;
void set(int x)
{
det=x;
Max=Min=x;
sum=(i64)(R-L+1)*x;
}
};
node a[N<<2];
int d
,next
,p
,b
;
int n;
void pushUp(int t)
{
if(a[t].L==a[t].R) return;
a[t].sum=a[t*2].sum+a[t*2+1].sum;
a[t].Min=min(a[t*2].Min,a[t*2+1].Min);
a[t].Max=max(a[t*2].Max,a[t*2+1].Max);
}
void pushDown(int t)
{
if(a[t].L==a[t].R) return;
int M=(a[t].L+a[t].R)>>1;
if(a[t].det>0)
{
a[t*2].set(a[t].det);
a[t*2+1].set(a[t].det);
a[t].det=0;
}
}
void build(int t,int L,int R)
{
a[t].L=L;
a[t].R=R;
a[t].det=0;
if(L==R)
{
a[t].Max=a[t].Min=a[t].sum=b[L];
return;
}
int M=(L+R)>>1;
build(t*2,L,M);
build(t*2+1,M+1,R);
pushUp(t);
}
void update(int t,int L,int R,int x)
{
if(L<=a[t].L&&a[t].R<=R&&x<a[t].Min)
{
a[t].set(x);
return;
}
pushDown(t);
int M=(a[t].L+a[t].R)>>1;
if(M>=L&&a[t*2].Max>x) update(t*2,L,R,x);
if(M<R&&a[t*2+1].Max>x) update(t*2+1,L,R,x);
pushUp(t);
}
int main()
{
Rush(n)
{
if(n==0) break;
int i,j=0;
clr(p,0);
FOR1(i,n)
{
RD(d[i]);
if(d[i]>n) d[i]=n+1;
p[d[i]]=1;
while(p[j]) j++;
b[i]=j;
}
for(i=0;i<=n+1;i++) p[i]=n+1;
for(i=n;i>=1;i--)
{
next[i]=p[d[i]];
p[d[i]]=i;
}
build(1,1,n);
i64 ans=a[1].sum;
FOR1(i,n)
{
if(i+1<=next[i]-1) update(1,i+1,next[i]-1,d[i]);
update(1,i,i,0);
ans+=a[1].sum;
}
printf("%I64d\n",ans);
}
}
相关文章推荐
- 使用Xcode和Instruments调试解决iOS内存泄露
- shell 往awk 中传值
- Android 圆形 ImageView
- AndroidAnnotations——Enhance custom classes 优化自定义类
- 排序是我曾经的朋友
- 【CCS仿真】用matlab把CCS保存的32位16进制的数据转换为十进制的数
- wpf 自定义窗口,最大化时不覆盖任务栏
- CyberArticle(网文快捕)上传文件提示‘许可不足’
- 花名册
- Android TextView中文字通过SpannableString来设置超链接、颜色、字体等属性
- iphone调用系统电话、浏览器、地图、邮件等
- android应用开发之判断wifi下局域网IP
- 两个数组去重的JS代码
- Windows7暗藏的管理功能之上帝模式如何调用
- hash类型数据的遍历
- C和C++语言的强制类型转换 Type Cast
- 推荐一个很全的偏编程方面的书籍网站《皮皮书屋》
- 项目3 带姓名的成绩单
- Redis导数据脚本
- java acm