• Codeforces 232D Fence


    D. Fence
    time limit per test
    3 seconds
    memory limit per test
    256 megabytes
    input
    standard input
    output
    standard output

    John Doe has a crooked fence, consisting of n rectangular planks, lined up from the left to the right: the plank that goes i-th (1 ≤ i ≤ n)(from left to right) has width 1 and height hi. We will assume that the plank that goes i-th (1 ≤ i ≤ n) (from left to right) has index i.

    piece of the fence from l to r (1 ≤ l ≤ r ≤ n) is a sequence of planks of wood with indices from l to r inclusive, that is, planks with indices l, l + 1, ..., r. The width of the piece of the fence from l to r is value r - l + 1.

    Two pieces of the fence from l1 to r1 and from l2 to r2 are called matching, if the following conditions hold:

    • the pieces do not intersect, that is, there isn't a single plank, such that it occurs in both pieces of the fence;
    • the pieces are of the same width;
    • for all i (0 ≤ i ≤ r1 - l1) the following condition holds: hl1 + i + hl2 + i = hl1 + hl2.

    John chose a few pieces of the fence and now wants to know how many distinct matching pieces are for each of them. Two pieces of the fence are distinct if there is a plank, which belongs to one of them and does not belong to the other one.

    Input

    The first line contains integer n (1 ≤ n ≤ 105) — the number of wood planks in the fence. The second line contains n space-separated integers h1, h2, ..., hn (1 ≤ hi ≤ 109) — the heights of fence planks.

    The third line contains integer q (1 ≤ q ≤ 105) — the number of queries. Next q lines contain two space-separated integers li and ri (1 ≤ li ≤ ri ≤ n) — the boundaries of the i-th piece of the fence.

    Output

    For each query on a single line print a single integer — the number of pieces of the fence that match the given one. Print the answers to the queries in the order, in which the queries are given in the input.

    Examples
    input
    10
    1 2 2 1 100 99 99 100 100 100
    6
    1 4
    1 2
    3 4
    1 5
    9 10
    10 10
    output
    1
    2
    2
    0
    2
    9

    【题目大意】:

    【官方题解】:

    Idea: Geraldtunyash Implementation: Moskupols Editorial: Moskupols

    Let d and d' be arrays such that di = hi - hi + 1, d'i =  - di for every 1 ≤ i ≤ (n - 1). With that notation the conditions of matching look somehow like these:

    • the pieces do not intersect, that is, there isn't a single plank, such that it occurs in both pieces of the fence;
    • the pieces are of the same width;
    • for all i i (0 ≤ i ≤ r1 - l1 - 1) the following condition holds: dl1 + i = d'l2 + i (that is true in case when l = r).

    The main idea of our solution is stated in the next sentence. For each query l...r the answer is number of pairs (a, b) such that (a > r orb < l), 1 ≤ a ≤ b ≤ n - 1, b - a = r - l and dl...r - 1 exactly matches d'a...b - 1. Let's build a suffix array sa from the concatenation of arraysd and d' with a fictive number between them for separation. Let position of suffix i in sa be posi. For each query all pieces of the fence that satisfy both second and third conditions of matching will be placed in sa on some segment boundleft...boundright such thatboundleft ≤ posl ≤ boundright and lcp(boundleft...boundright) ≥ (r - l). So, it's possible to use binary search to find bound's. Depending on complexity of lcp finding algorithm, we could get them in O(logn) or O(log2n) complexity.

    But there is still a problem to count the number of suffixes from saboundleft...boundright that satisfy the first condition too. Actually it is equal to count the number of i (boundleft ≤ i ≤ boundright) such that (n + 1 ≤ sai ≤ n + l - (r - l) - 1 or sai ≥ n + r) (in the concatenation d'starts from n + 1). It is a classic problem to count numbers from the given interval in the given subarray. For each query it could be solved in O(logn) complexity.

    For instance, we could solve it offline using sweep line method and any data structure that support queries of sum on an interval and increment of an element. Or we could use some 2D/persistent structure.

    So, the summary of the algorithm looks like this:

    • build d and d'. Build a suffix array on their concatenation.

    For each query:

    • find the interval (boundleft...boundright) with two consecutive binary searches using lcp function.
    • query the count of suffixes from that interval that do not intersect with the given piece of the fence.

    The best author's solution complexity is O(nlogn + qlogn), but careful written solutions in O(nlog2n) comply with the lime limit too.

    【私人题解】:

    采用O(nlog2n)算法:

      条件可以看成是这样的:

        对于0<i<s,A[l1+i]+A[l2+i]=A[l1+i-1]+A[l2+i-1]。

        移项,得到A[l1+i]-A[l1+i-1]=A[l2+i-1]-A[l2+i]。

      那不就可以看成这个数组的差分数组和差分数组中每一项取相反数得到的数组的长度为S的公共子串吗?(当然还有条件1的限制)

      对于每次询问,我们找到l1起始的后缀的排名,即rank[l1],向上向下二分出lcp(up,low)≥S的区间,然后相当于在这个区间中查询满足[sa[i],sa[i]+s-1]不与[l1,l1+s-1]相交的后缀的个数。每次二分,之前离线用BIT存贮前缀和,计算两次即可。

    【参考代码】:

    //by shenben
    //g++ Rank 是关键字 
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define lowbit(x) (x&-x)
    using namespace std;
    const int N=2e5+5;
    struct node{int l,r,id;}q[N];int Q,ans[N];
    int n,m,c[N],h[N],sa[N],tsa[N],Rank[N],tRank[N];
    int log2[N],f[N][20];
    int s[N],a[N],BIT[N];
    //==========快读=============================
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    } 
    //==========Suffix Array=====================
    inline void DA(int maxx=256){
        int p;
        for(int i=0;i<=maxx;i++) c[i]=0;
        for(int i=1;i<=n;i++) c[Rank[i]=s[i]]++;
        for(int i=2;i<=maxx;i++) c[i]+=c[i-1];
        for(int i=n;i;i--) sa[c[Rank[i]]--]=i;
        tRank[sa[1]]=p=1;
        for(int i=2;i<=n;i++){
            if(Rank[sa[i]]!=Rank[sa[i-1]]) p++;
            tRank[sa[i]]=p;
        }
        for(int i=1;i<=n;i++) Rank[i]=tRank[i];
        for(int k=1;p<n;k<<=1,maxx=p){
            p=0;
            for(int i=n-k+1;i<=n;i++) tsa[++p]=i;
            for(int i=1;i<=n;i++) if(sa[i]>k) tsa[++p]=sa[i]-k;
            for(int i=0;i<=maxx;i++) c[i]=0;
            for(int i=1;i<=n;i++) tRank[i]=Rank[tsa[i]];
            for(int i=1;i<=n;i++) c[tRank[i]]++;
            for(int i=2;i<=maxx;i++) c[i]+=c[i-1];
            for(int i=n;i;i--) sa[c[tRank[i]]--]=tsa[i];
            tRank[sa[1]]=p=1;
            for(int i=2;i<=n;i++){
                if(Rank[sa[i]]!=Rank[sa[i-1]]||Rank[sa[i]+k]!=Rank[sa[i-1]+k]) p++;
                tRank[sa[i]]=p;
            }
            for(int i=1;i<=n;i++) Rank[i]=tRank[i];
        }
        for(int i=1,k=0;i<=n;i++){
            int j=sa[Rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            h[Rank[i]]=k;if(k>0) k--;
        }
    }
    //==========RMQ==============================
    inline void RMQ(){
        for(int i=1;i<=n;i++) f[i][0]=h[i];
        for(int i=2;i<=n;i++) log2[i]=log2[i>>1]+1;
        for(int j=1;j<=log2[n];j++){
            for(int i=1;i+(1<<j)-1<=n;i++){
                f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
            }
        }
    }
    inline int query(int l,int r){
        int k=log2[r-l+1];
        return min(f[l][k],f[r-(1<<k)+1][k]);
    }
    //==========排序============================== 
    inline bool cmp1(const node &a,const node &b){
        return a.l+a.r<b.l+b.r;
    }
    inline bool cmp2(const node &a,const node &b){
        return a.l-a.r<b.l-b.r;
    }
    //==========BIT统计答案=======================
    inline void add(int x,int v){
        for(int i=x;i<=n;i+=lowbit(i)) BIT[i]+=v;
    }
    inline int get(int x){
        int res=0;
        for(int i=x;i;i-=lowbit(i)) res+=BIT[i];
        return res;
    }
    //==========向上、向下二分====================
    inline int solve(int pos,int k){
        int tl=pos+1,tr=pos-1;
        int l=pos+1,r=n,mid;
        while(l<=r){
            mid=l+r>>1;
            if(query(pos+1,mid)>=k) l=mid+1,tr=mid;
            else r=mid-1;
        }
        l=1;r=pos;
        while(l<=r){
            mid=l+r>>1;
            if(query(mid+1,pos)>=k) r=mid-1,tl=mid;
            else l=mid+1;
        }
        return get(tr)-get(tl-1);
    }
    int main(){
        m=read();
        for(int i=1;i<=m;i++) a[i]=read();
        /*转成差分序列*/
        for(int i=1;i<m;i++) s[i]=a[i+1]-a[i];s[m]=1e9;
        for(int i=1;i<m;i++) s[i+m]=-s[i];
        n=(m<<1)-1;
        /*离散化*/
        memcpy(a,s,n+1<<2);
        sort(a+1,a+n+1);
        int cnt=unique(a+1,a+n+1)-(a+1);
        for(int i=1;i<=n;i++) s[i]=lower_bound(a+1,a+cnt+1,s[i])-a;
        
        DA(cnt);RMQ();
        
        Q=read();
        for(int i=1;i<=Q;i++){
            q[i].l=read();q[i].r=read();q[i].r-=q[i].l;
            q[i].id=i;
        }
        /*离线统计答案:右边从后扫,左边从前扫*/
        sort(q+1,q+Q+1,cmp1);
        for(int j=m-1,i=Q;i;i--){
            for(;j>1&&j>q[i].l+q[i].r;j--) add(Rank[j+m],1);
            ans[q[i].id]+=solve(Rank[q[i].l],q[i].r);
        }
        sort(q+1,q+Q+1,cmp2);
        memset(BIT,0,n+1<<2);
        for(int j=1,i=1;i<=Q;i++){
            for(;j<m&&j+q[i].r<q[i].l;j++) add(Rank[j+m],1);
            ans[q[i].id]+=solve(Rank[q[i].l],q[i].r);
            if(!q[i].r) ans[q[i].id]=m-1;
        }
        for(int i=1;i<=Q;i++) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    值币转换编程总结
    打印沙漏编程总结
    Leetcode每日一题 面试题 17.21. 直方图的水量
    VS2017-OPENGL配置glfw+glad
    OpenGL(二) 绘制一个三角形
    OpenGL(一) 渲染循环创建窗口
    Leetcode每日一题 1006.笨阶乘
    Leetcode每日一题 90.子集 II
    Leetcode每日一题 190.颠倒二进制位
    Leetcode 第243场周赛 创新奇智&力扣
  • 原文地址:https://www.cnblogs.com/shenben/p/6596985.html
Copyright © 2020-2023  润新知