codeforces 193 D Two Segments
discription
Nick has some permutation consisting of p integers from 1 to n. A segment [l,?r] (l?≤?r) is a set of elements pi satisfying l?≤?i?≤?r.
Nick calls a pair of segments [a0,?a1] and [b0,?b1] (1?≤?a0?≤?a1?<?b0?≤?b1?≤?n) good if all their (a1?-?a0?+?b1?-?b0?+?2) elements, when sorted in ascending order, form an arithmetic progression with a difference of 1. That is, when they sorted in ascending order, the elements are in the form {x
Your task is to find the number of distinct pairs of good segments in the given permutation. Two pairs of segments are considered distinct if the sets of elements contained in these pairs of segments are distinct. For example, any segment [l,?r](l?<?r
See the notes accompanying the sample tests for clarification.
Input
The first line contains integer n (1?≤?n?≤?3·105) — the permutation size. The second line contains n
Output
Print a single integer — the number of good pairs of segments of permutation p.
Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.
Example
Input3Output
1 2 3
3Input
5Output
1 4 5 3 2
10Input
5Output
5 4 3 1 2
10
Note
In the first sample the following pairs of segments are good: ([1,?1], [2,?2]); ([2,?2], [3,?3]); ([1,?2], [3,?3]). Pair of segments ([1,?1], [2,?3]) is by definition equivalent to pair ([1,?2], [3,?3]), since both of them covers the same set of elements, namely {1,?2,?3}.
In the third sample the following pairs of segments are good: ([4,?4], [5,?5]); ([3,?3],[4,?5]); ([2,?2],[3,?5]); ([1,?1],[2,?5]); ([3,?3],[5,?5]); ([2,?3],[5,?5]); ([1,?3],[5,?5]); ([2,?2],[3,?3]); ([1,?1],[2,?3]); ([1,?1],[2,?2]).
題目大意就是給出一個排列,要求選出一組連續的數,使得它們在原序列中是連續的一段或者兩段,求方案數。
乍一看感覺毫無頭緒,那麽手算一下樣例看看吧。
第三個樣例中,序列是:5,4,3,1,2。
我們先假設一組連續的數中最小值是1,再枚舉連續的數的長度。
當1加進來的時候,因為1的左邊和右邊都沒加進來,所以原序列中的段數從0->1;
當2加進來的時候,因為左邊的1已經加進來了,而右邊沒有數(相當於沒有加進來),所以段數不變;
3,4,5的情況類似。
但還少了一種情況,那就是一個數如果在原序列中兩邊的數都已經加進選的數中時,那麽總段數要-1。
這樣我們可以O(N)預處理出當最小數是1時,把每個數加進選的集合中造成的影響。
這樣再處理一下前綴和,就可以得到加進這個數之後的總段數個數,從而知道哪些
選數集合是可以加到答案裏的了。
那麽怎麽根據這個快速維護和計算最小數是2,3,4....,n(其實不能是n,因為至少要選2個數)的方案數呢?
答案是掃描法。
接下來就不劇透題解了。
只需要想出如何維護最小值+1的影響就可以了。
(一個小錯(把下標寫成數)調了我一個多點gg)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #define ll long long #define maxn 300005 #define inf 10000000 using namespace std; struct node{ int val,s; }p[5]; bool cmp(node x,node y){ return x.val<y.val; } ll ans=0; bool v[maxn]; int le,ri,w; int n,m,a[maxn],qz[maxn],dy[maxn]; int mn[maxn<<2|1][2],sum[maxn<<2|1][2],tag[maxn<<2|1]; inline void pushdown(int o,int lc,int rc){ if(tag[o]){ tag[lc]+=tag[o],tag[rc]+=tag[o]; mn[lc][0]+=tag[o],mn[lc][1]+=tag[o]; mn[rc][0]+=tag[o],mn[rc][1]+=tag[o]; tag[o]=0; } } inline void merge(int o,int lc,int rc){ sum[o][0]=sum[o][1]=0; p[1]=(node){mn[lc][0],sum[lc][0]}; p[2]=(node){mn[lc][1],sum[lc][1]}; p[3]=(node){mn[rc][0],sum[rc][0]}; p[4]=(node){mn[rc][1],sum[rc][1]}; sort(p+1,p+5,cmp); int now=-1; for(int i=1;i<=4;i++){ if(p[i].val>p[i-1].val) now++; if(now>1) break; mn[o][now]=p[i].val; sum[o][now]+=p[i].s; } } void build(int o,int l,int r){ if(l==r){ mn[o][0]=qz[l],mn[o][1]=inf; sum[o][0]=sum[o][1]=1; return; } int mid=l+r>>1,lc=o<<1,rc=(o<<1)|1; build(lc,l,mid),build(rc,mid+1,r); merge(o,lc,rc); } void update(int o,int l,int r){ if(l>=le&&r<=ri){ tag[o]+=w; mn[o][0]+=w,mn[o][1]+=w; return; } int mid=l+r>>1,lc=o<<1,rc=(o<<1)|1; pushdown(o,lc,rc); if(le<=mid) update(lc,l,mid); if(ri>mid) update(rc,mid+1,r); merge(o,lc,rc); } int query(int o,int l,int r){ if(l>=le&&r<=ri){ int an=0; if(mn[o][0]<=2){ an+=sum[o][0]; if(mn[o][1]<=2) an+=sum[o][1]; } return an; } int mid=l+r>>1,lc=o<<1,rc=(o<<1)|1,an=0; pushdown(o,lc,rc); if(le<=mid) an+=query(lc,l,mid); if(ri>mid) an+=query(rc,mid+1,r); return an; } int main(){ p[0]=(node){-100,-100}; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",a+i),dy[a[i]]=i; for(int i=1;i<=n;i++){ int pos=dy[i]; qz[i]=1,v[pos]=1; if(v[pos-1]) qz[i]--; if(v[pos+1]) qz[i]--; qz[i]+=qz[i-1]; } build(1,1,n); for(int i=1;i<=n;i++){ le=i+1,ri=n; if(le<=ri) ans+=(ll)query(1,1,n); int po1=0,po2=0,pos=dy[i]; if(pos>1&&a[pos-1]>i) po1=a[pos-1]; if(pos<n&&a[pos+1]>i) po2=a[pos+1]; w=-1,le=i+1,ri=n; if(le<=ri) update(1,1,n); if(po1){ le=po1,ri=n,w=1; update(1,1,n); } if(po2){ le=po2,ri=n,w=1; update(1,1,n); } } printf("%I64d\n",ans); return 0; }
codeforces 193 D Two Segments