• 【暴力Treap 或 离线归并】子串计数(genies)


    子串计数(genies)

    Description

    给出一段含有n个元素的序列a,要求求出子串和小于等于t的子串个数

    Input Data

    输入共两行
    第一行包含两个整数,n,t分别表示序列a元素的个数和限制t
    第二行包含n个数表示元素a_i

    Output Data

    共一行,含一个数
    表示子串和小于等于t的子串个数。

     Input / Output Sample

    input #1:

    5 4 5 -1 3 4 -1

    output #1:
    7

    Solution:

    一道妙题,首先分析性质。如果从L+1到R可以构成一个合法的区级显然s[R]-S[L]<=t,且L<=R;

    前缀和的想法是显然的,但是还是突破不了枚举子串的瓶颈。

    假设当前枚举到第i个元素那么我要从s[0]-s[i-1]找到尽可能多的s[j] (j∈[0,i-1])使s[i]-s[j]<=t,不妨考虑移项

    s[j]>=s[i]-t,而此时s[i]-t是定值!!!我们只需要统计前面的s[j]有多少个s[j]>=s[i]-t就行了!即求s[i]-t的排行rank,

    n-rank就是答案!

    然而这种方法不是很妙,更妙的方法是这样的:

    首先我们知道归并排序只会把他分成越分越小,而不会改变他原序列的前后顺序。

    对于每一次归并的[L,T]和[T+1,R],在每一段都是升序排序的,我们弄一个指针pt1指在[L,T]弄另外的指针pt2指在[T+1,R]

    对于s[pt1]不断的把pt2往右移动,找到第一个不能满足s[pt2]-s[pt1]<=t的点(前面可以满足的记录)。这样可以保证处理2段区间的复杂度是O(n)的

    对于全部的数据复杂度显然是O(n log n)的。

    Code:

    # include <bits/stdc++.h>
    # define int long long
    using namespace std;
    const int N=200020;
    int n;
    int t,ans=0; 
    int s[N];
    void solve(int l,int r)
    {
        if (l>=r) return;
        int mid=(l+r)>>1;
        solve(l,mid); solve(mid+1,r);
        int ret=0;
        for (int i=l,j=mid;i<=mid;i++) {
            while (j<r&&s[j+1]-s[i]<=t) j++;
            ret+=j-mid;
        }
        ans+=ret;
        inplace_merge(s+l,s+mid+1,s+r+1);
    }
    signed main()
    {
        scanf("%lld%lld",&n,&t);
        for (int i=1;i<=n;i++) {
            int x; scanf("%lld",&x);
            s[i]=s[i-1]+x;
        }
        solve(0,n);
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    6Linux用户身份与文件权限
    5Linux流程控制语句-if、for、while、case语句、计划任务
    4Linux环境变量、Vim、Shell脚本
    3Linux常用命令
    2Linux常用命令-Liunu就该这么学
    1安装Linux
    Citrix XenApp工作原理
    Citrix XenApp登录服务器过程详解
    0初识Linux
    我的电脑-磁盘 不显示菜单栏和工具栏解决方法
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/10335838.html
Copyright © 2020-2023  润新知