Codeforces 526F 分治

说实话这题挺简单的(知道题解以后),但是一般人做的时候脑洞不大一点还不一定做得出来。
显然,题目可化简为:给定 N 个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。
进一步可以化为:有多少种情况使得,相邻的 k 个数中最大值和最小值的差小于等于 k-1。
大致有两种解法,一种是分治,一种是线段树。
这里主要讲一下分治的解法。
考虑分治,对于当前区间[L,R],记区间中点为 mid。当前区间的答案就是Ans[L..mid]+Ans[mid+1..R]+跨过中点的合法区间数,然后就分为两种情况了:
1.最小值和最大值在同侧。
2.最小值和最大值在异侧。
下面只考虑最值同在左,和最小值在左,最大值在右的情况。
因为其余两种是对称的。
对于最值同在左侧的情况,我们O(N)枚举左边界在哪,然后可以
计算出右边界的位置,在判断是否合法,统计答案。对于最小值在左侧,最大值在右侧的情况,如果一个区间满足我们所要求的关系的话,就一定有:
max(a[mid +1]…a[right]) - min(a[left]…a[mid]) =right- left
移项可得
max(a[mid +1]…a[right]) - right = min(a[left]…a[mid]) -left
然后可以用单调栈来完成这个任务。 如果加一些黑科技可以大大减少代码量,但是复杂度会多一个 log。
简单提一下,线段树解法的思路大致也是维护一个单调栈,然后进行区间修改和查询,统计答案。
蒟蒻还在用Pas,虽然偶尔也用C艹

uses math;
const maxn=300010;

var
    n,i,j,k,p,m,x:longint;
    maxm,minm,num:array[0..maxn]of longint;
    f:array[0..maxn shl 1]of longint;
    ans:longint;
procedure fz(l,r:longint);
var
    mid,i,j,l1,r1:longint;
begin
    mid:=(l+r)>>1;
    minm[mid]:=num[mid];
    maxm[mid]:=num[mid];
    maxm[mid+1]:=num[mid+1];
    minm[mid+1]:=num[mid+1];
    for i:=mid+2 to r do
    begin
        maxm[i]:=max(num[i],maxm[i-1]);
        minm[i]:=min(num[i],minm[i-1]);
    end;
    for i:=mid-1 downto l do
    begin
        maxm[i]:=max(num[i],maxm[i+1]);
        minm[i]:=min(num[i],minm[i+1]);
    end;
    for i:=mid downto l do
    begin
        r1:=i+maxm[i]-minm[i];
        if (r1>mid)and(r1<=r)and(minm[r1]>minm[i])and(maxm[r1]then inc(ans);
    end;
    for i:=mid+1 to r do
    begin
        l1:=i-maxm[i]+minm[i];
        if (l1>=l)and(l1<=mid)and(minm[l1]>minm[i])and(maxm[l1]then
        inc(ans);
    end;
        l1:=mid+1;
        r1:=l1;
        for i:=mid downto l do
        begin
            while (r1<=r)and(minm[r1]>minm[i])do
            begin
                inc(f[maxm[r1]-r1+n]);
                inc(r1);
            end;
            while(l1and(maxm[l1]do
            begin
                dec(f[maxm[l1]-l1+n]);
                inc(l1);
            end;
            inc(ans,f[minm[i]-i+n]);
        end;
        for i:=l1 to r1-1 do
        dec(f[maxm[i]-i+n]);
        l1:=mid;
        r1:=l1;
        for i:=mid+1 to r do
        begin
            while (l1>=l)and(minm[l1]>minm[i])do
            begin
                inc(f[maxm[l1]+l1]);
                dec(l1);
            end;
            while (r1>l1)and(maxm[r1]do
            begin
                dec(f[maxm[r1]+r1]);
                dec(r1);
            end;
            inc(ans,f[minm[i]+i]);
        end;
        for i:=r1 downto l1+1 do dec(f[maxm[i]+i]);
        if (lthen
        begin
            fz(l,mid);
            fz(mid+1,r);
        end;
end;
begin
    read(n);
    for i:=1 to n do
    begin
        read(x,num[x]);
    end;
    fz(1,n);
    writeln(ans+n);
end.

你可能感兴趣的:(做题小结,codeforces,分治)