HDU4747——2013 ACM/ICPC Asia Regional Hangzhou Online

啦啦啦。

这是杭州网赛的一个题目,当时没做出来,当然这个想法确实比较难想到。

题目质量很高,这个题目也很特别,以前都没做过类似的题目。让我又一次体验了线段树的强大力量。

题目的意思是给你n个数a1-an。对于任何一个区间[l,r],它所对应的值为这个区间内没有出现的最小非负整数,求所有1<=L<=R<=n的区间所对应的值的总和。

比赛的时候苦思无果,以为是什么高端的数据结构或者很奇怪的算法,后来才发现自己坑了。

题目其实是这样的,在最开始预处理所有1开头的(即L=1的所有区间的值,然后从1开始每次都删除一个数,并且更新区间,每次都求一次和,然后就没有然后了,把每次更新后的区间所要求的和加起来就是答案了哦。

这个想法是没有错的,但是实现起来十分的麻烦。我也是经历了若干发TLE,WA,RE以后最终内牛满面地A掉了此题,以此作为纪念。

再讲讲具体是怎么实现的吧!其实预处理的话可以用数组模拟链表来实现,时间非常快(就像建图那种方法),但是由于Ai可能给的很大,所以我们用哈希来实现快速查询啊。记录每个数下一次出现的位置。如果没有出现,那么下一次出现的位置为n+1就好了。

假设当前我已经统计好了Ai作为起点的值,现在要统计Ai+1作为起点的值?应该怎么做?怎么更新呢?

是这样的。找到Ai+1下一次出现的地方的前一位。如果它所对应的那个函数值比当前的小,那说明不用更新了(想想问什么?因为说明有比当前数更小的数没有出现,所以无需更新,而在出现以后的地方显然又已经存在Ai在前面了。)

如果那个数的值大于当前的值,则说明可以修改更新。但是在更新前要先查一下从哪一步开始查找,就二分查找啦,找到第一个大于Ai的数。然后更新区间就可以了。

记得每次删除一个数Ai都要把query(i+1,pos)  的值加到ans里面哦。

总的时间复杂度是:n*log(n)*log(n)。Dangerous !!!

上代码吧:(注意用long long)

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <map>
  5 #define maxn 200200
  6 #define ll long long
  7 using namespace std;
  8 
  9 int a[maxn],b[maxn],col[4*maxn],next[maxn],cur;
 10 int n,m,k,t,pos;
 11 ll ans,sum[4*maxn];
 12 map<int,int> ss;
 13 bool visit[maxn];
 14 
 15 void PushDown(int rt,int l,int r)
 16 {
 17     if (col[rt]==-1) return;
 18     col[rt<<1]=col[rt<<1|1]=col[rt];
 19     int mid=(l+r)>>1;
 20     sum[rt<<1]=col[rt]*(mid-l+1);
 21     sum[rt<<1|1]=col[rt]*(r-mid);
 22     col[rt]=-1;
 23 }
 24 
 25 void PushUp(int rt)
 26 {
 27     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
 28 }
 29 
 30 void build(int rt,int l,int r)
 31 {
 32     col[rt]=-1;
 33     if (l==r)  { sum[rt]=b[l]; return ; }
 34     int mid=(l+r)>>1;
 35     build(rt<<1,l,mid);
 36     build(rt<<1|1,mid+1,r);
 37     PushUp(rt);
 38 }
 39 
 40 void update(int rt,int l,int r,int L,int R,int id)
 41 {
 42     if (L>R) return;
 43     if (L<=l && R>=r)
 44     {
 45         sum[rt]=id*(r-l+1);
 46         col[rt]=id;
 47         return ;
 48     }
 49     PushDown(rt,l,r);
 50     int mid=(l+r)>>1;
 51     if (L<=mid) update(rt<<1,l,mid,L,R,id);
 52     if (R> mid) update(rt<<1|1,mid+1,r,L,R,id);
 53     PushUp(rt);
 54 }
 55 
 56 ll query(int rt,int l,int r,int L,int R)
 57 {
 58     if (L>R) return 0;
 59     if (L<=l && R>=r) return sum[rt];
 60     PushDown(rt,l,r);
 61     int mid=(l+r)>>1;
 62     ll tot=0;
 63     if (L<=mid) tot=query(rt<<1,l,mid,L,R);
 64     if (R> mid) tot+=query(rt<<1|1,mid+1,r,L,R);
 65     return tot;
 66 }
 67 
 68 int find(int l,int r,int id)
 69 {
 70     int mid;
 71     while (l<r)
 72     {
 73         mid=(l+r)>>1;
 74         if (query(1,1,n,mid,mid)<=id) l=mid+1;
 75             else r=mid;
 76     }
 77     return l;
 78 }
 79 
 80 int main()
 81 {
 82     while (scanf("%d",&n) && (n))
 83     {
 84         ss.clear();
 85         for (int i=1; i<=n; i++) scanf("%d",&a[i]);
 86         memset(visit,false,sizeof visit);
 87         for (int i=n; i>=1; i--)
 88         {
 89             if (ss[a[i]]!=0) next[i]=ss[a[i]];
 90                 else next[i]=n+1;
 91             ss[a[i]]=i;
 92         }
 93         cur=0;
 94         for (int i=1; i<=n; i++)
 95         {
 96             if (a[i]<maxn) visit[a[i]]=true;
 97             while (visit[cur]) cur++;
 98             b[i]=cur;
 99         }
100         build(1,1,n);
101         ans=sum[1];
102         for (int i=1; i<n; i++)
103         {
104             pos=min(next[i]-1,n);
105             if (i+1<=pos)
106                 if (query(1,1,n,pos,pos)>a[i])
107                 {
108                     k=find(i+1,pos,a[i]);
109                     update(1,1,n,k,pos,a[i]);
110                 }
111             ans+=query(1,1,n,i+1,n);
112         }
113         printf("%I64d\n",ans);
114     }
115     return 0;
116 }

 

转载于:https://www.cnblogs.com/lochan/p/3349131.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值