codeforces 997E(线段树)
Posted wmrv587
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforces 997E(线段树)相关的知识,希望对你有一定的参考价值。
分析:
首先考虑如何计算整个数组有多少个good区间
容易发现一个区间是good区间当且仅当max-min-len=-1,且任意区间max-min-len>=-1
我们可以枚举右端点,然后维护前面每个位置到当前右端点的max-min-len值,容易发现我们只需要维护区间最小值和最小值的个数就行了,于是用线段树即可
于是我们可以得到以某个点为右端点的时候合法区间总数,那么我们把每次的结果加起来,就得到了整个数组有多少个good区间
每次右端点移动怎么更新呢?显然我们只需要用一个max单调栈,一个min单调栈来帮助寻找修改区间,容易知道修改区间的个数是O(n)级别的,所以整个修改是O(nlogn)的
好,现在我们回到原来的问题,如何求[l,r]内有多少个good区间
在这个问题中,我们实际上要额外知道“每个区间的历史答案和”,这是什么意思呢,比如说在i=7的时候,[4,7]里的答案存的是有多少个合法的位置是和7匹配的
但i=6的时候,[4,7]里的答案存的是有多少个合法的位置是和6匹配的
我们自然而然就考虑到把每个右端点时刻,一个区间的产生的答案值加起来就行了,但这个时间复杂度是巨大的
这里我们仍然可以用延迟标记来解决,比如右端点i枚举完了,我们可以给[1,i]上个tag,表示这整个区间的历史答案需要加上现在时刻的答案
时间复杂度O(nlogn)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mp make_pair 4 const int maxn=120000; 5 struct wjmzbmr 6 { 7 int mi,num,tag; 8 int time; 9 long long ans; 10 }tree[maxn*4+5]; 11 vector<pair<int,int> > q[maxn+5]; 12 long long ans[maxn+5]; 13 int a[maxn+5],s[maxn+5],s1[maxn+5]; 14 int len,len1; 15 int n,m; 16 void addtag(int k,int x) 17 { 18 tree[k].tag+=x; 19 tree[k].mi+=x; 20 } 21 void addtime(int k,int x) 22 { 23 tree[k].time+=x; 24 tree[k].ans+=1LL*x*tree[k].num; 25 } 26 void pushdown(int k) 27 { 28 if(tree[k].tag) 29 { 30 addtag(k*2,tree[k].tag); 31 addtag(k*2+1,tree[k].tag); 32 tree[k].tag=0; 33 } 34 if(tree[k].time) 35 { 36 if(tree[k].mi==tree[k*2].mi) addtime(k*2,tree[k].time); 37 if(tree[k].mi==tree[k*2+1].mi) addtime(k*2+1,tree[k].time); 38 tree[k].time=0; 39 } 40 } 41 void update(int k) 42 { 43 tree[k].mi=min(tree[k*2].mi,tree[k*2+1].mi); 44 tree[k].ans=tree[k*2].ans+tree[k*2+1].ans; 45 tree[k].num=0; 46 if(tree[k].mi==tree[k*2].mi) tree[k].num+=tree[k*2].num; 47 if(tree[k].mi==tree[k*2+1].mi) tree[k].num+=tree[k*2+1].num; 48 } 49 void build(int k,int l,int r) 50 { 51 if(l>r) return; 52 if(l==r) 53 { 54 tree[k].num=1; 55 tree[k].mi=-1; 56 return; 57 } 58 int mid=(l+r)>>1; 59 build(k*2,l,mid); 60 build(k*2+1,mid+1,r); 61 update(k); 62 } 63 void modify(int k,int l,int r,int ql,int qr,int x) 64 { 65 if(r<ql||l>qr||l>r) return; 66 if(l>=ql&&r<=qr) 67 { 68 addtag(k,x); 69 return; 70 } 71 if(l==r) return; 72 pushdown(k); 73 int mid=(l+r)>>1; 74 modify(k*2,l,mid,ql,qr,x); 75 modify(k*2+1,mid+1,r,ql,qr,x); 76 update(k); 77 } 78 void modify1(int k,int l,int r,int ql,int qr,int x) 79 { 80 if(r<ql||l>qr||l>r) return; 81 if(l>=ql&&r<=qr) 82 { 83 if(tree[k].mi==-1) 84 addtime(k,x); 85 return; 86 } 87 if(l==r) return; 88 pushdown(k); 89 int mid=(l+r)>>1; 90 modify1(k*2,l,mid,ql,qr,x); 91 modify1(k*2+1,mid+1,r,ql,qr,x); 92 update(k); 93 } 94 long long query(int k,int l,int r,int ql,int qr) 95 { 96 if(r<ql||l>qr||l>r) return 0; 97 if(l>=ql&&r<=qr) return tree[k].ans; 98 if(l==r) return 0; 99 pushdown(k); 100 int mid=(l+r)>>1; 101 long long ans=query(k*2,l,mid,ql,qr)+query(k*2+1,mid+1,r,ql,qr); 102 update(k); 103 return ans; 104 } 105 int main() 106 { 107 scanf("%d",&n); 108 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 109 scanf("%d",&m); 110 for(int i=1;i<=m;++i) 111 { 112 int l,r; 113 scanf("%d%d",&l,&r); 114 q[r].push_back(mp(l,i)); 115 } 116 build(1,1,n); 117 for(int i=1;i<=n;++i) 118 { 119 while(len&&a[s[len]]<a[i]) modify(1,1,n,s[len-1]+1,s[len],a[i]-a[s[len]]),--len; 120 s[++len]=i; 121 //for(int j=1;j<=len;++j) printf("%d ",a[s[j]]);printf(" "); 122 while(len1&&a[s1[len1]]>a[i]) modify(1,1,n,s1[len1-1]+1,s1[len1],a[s1[len1]]-a[i]),--len1; 123 s1[++len1]=i; 124 //for(int j=1;j<=len1;++j) printf("%d ",a[s1[j]]);printf(" "); 125 modify1(1,1,n,1,i,1); 126 for(auto x:q[i]) ans[x.second]=query(1,1,n,x.first,i); 127 //printf("%lld ",query(1,1,n,1,i)); 128 //if(i==4) printf("%d ",tree[2].ans); 129 modify(1,1,n,1,i,-1); 130 131 } 132 for(int i=1;i<=m;++i) printf("%lld ",ans[i]); 133 return 0; 134 }
以上是关于codeforces 997E(线段树)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces 666E Forensic Examination SAM+权值线段树