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 }