51Nod 1555 布丁怪

来源:互联网 发布:hadoop 端口 编辑:程序博客网 时间:2024/04/28 02:21

分治

分治的方法很巧妙,就是写起来有一点小恶心

先放CF题解:http://codeforces.com/blog/entry/17281

题意就是问有多少连续区间满足区间内数字连续,这类序列计数一般考虑分治。

一个分治区间[l,r],考虑怎么统计过mid的答案。

暴力枚举左端点,然而右端点并不单调,不太好维护,考虑其他方法。

出现上述情况,当且仅当已经有合法的跨mid的区间[a,b],然后b右边下一个元素可以补充在连续区间的左右两侧,也就是区间可以扩张。主要原因是最大最小值会变化。

那就大力分类讨论最大最小值。最大最小值同侧的,单调枚举即可。对于异侧的,除了考虑到b-a=mx[b]-mi[a]或b-a=mx[a]-mi[b]之外,还有的限制是区间内不能有超过极值的值,这个限制导致随着左端点左移,右边的贡献区间的两个端点都是单调右移的,维护一下即可。

#include<cstdio>#include<algorithm>#define N 300005using namespace std;namespace runzhe2000{    typedef long long ll;    const int INF = 1<<29;     int read()    {        int r = 0; char c = getchar();        for(; c < '0' || c > '9'; c = getchar());        for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());        return r;    }    void mswap(int &a, int &b){int t = a; a = b; b = t;}    int n, a[N], mi[N], mx[N], sum[N<<1], sumR[N<<1], sumL[N<<1];    ll ans;    void dc(int l, int r)    {        if(l == r) {ans++; return;}  int mid = (l+r)>>1;         mi[mid] = mx[mid] = a[mid]; for(int i = mid-1; i >= l; i--) mi[i] = min(a[i], mi[i+1]), mx[i] = max(a[i], mx[i+1]);        mi[mid+1] = mx[mid+1] = a[mid+1]; for(int i = mid+2; i <= r; i++) mi[i] = min(a[i], mi[i-1]), mx[i] = max(a[i], mx[i-1]);        // min & max in L        int curl = mid, curr = mid;        for(; curl >= l; curl--)        {            for(; curr < r && mi[curl] < a[curr+1] && a[curr+1] < mx[curl]; curr++);            if(curl < mid && curr > mid  && curr - curl == mx[curl] - mi[curl]) ans++;        }        // min & max in R        curl = mid+1, curr = mid+1;        for(; curr <= r; curr++)        {            for(; curl > l && mi[curr] < a[curl-1] && a[curl-1] < mx[curr]; curl--);            if(curl < mid+1 && curr > mid+1 && curr - curl == mx[curr] - mi[curr]) ans++;        }        // min in L, max in R        {            for(int i = mid+1; i <= r; i++) sum[mx[i]-i+n]++;            int L = mid+1, R = mid;            for(int i = mid+1; i <= r; i++) sumL[mx[i]-i+n]++;            for(int i = mid; i >= l; i--)            {                for(; R < r && mi[i] < mi[R+1]; R++) sumR[mx[R+1]-(R+1)+n]++;                for(; L <= r && mx[i] > mx[L] ; L++) sumL[mx[L]-L+n]--;                if(L <= R) ans += sumL[mi[i]-i+n] + sumR[mi[i]-i+n] - sum[mi[i]-i+n];            }            for(int i = mid+1; i <= R; i++) sumR[mx[i]-i+n]--;            for(int i = L; i <= r; i++) sumL[mx[i]-i+n]--;            for(int i = mid+1; i <= r; i++) sum[mx[i]-i+n]--;        }        // max in L, min in R        {            for(int i = mid+1; i <= r; i++) sum[mi[i]+i]++;            int L = mid, R = mid+1;            for(int i = mid+1; i <= r; i++) sumR[mi[i]+i]++;            for(int i = mid; i >= l; i--)            {                for(; R <= r && mi[i] < mi[R]; R++) sumR[mi[R]+R]--;                for(; L < r && mx[i] > mx[L+1] ; L++) sumL[mi[L+1]+(L+1)]++;                if(R <= L) ans += sumL[mx[i]+i] + sumR[mx[i]+i] - sum[mx[i]+i];            }            for(int i = mid+1; i <= L; i++) sumL[mi[i]+i]--;            for(int i = R; i <= r; i++) sumR[mi[i]+i]--;            for(int i = mid+1; i <= r; i++) sum[mi[i]+i]--;        }               dc(l,mid); dc(mid+1, r);    }     void main()    {        n = read();        for(int i = 1, j; i <= n; i++) j = read(), a[j] = read();        dc(1,n); printf("%lld\n",ans);    }}int main(){    runzhe2000::main();}
0 0