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

Posted ljc20020730

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暴力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;
}

 

以上是关于暴力Treap 或 离线归并子串计数(genies)的主要内容,如果未能解决你的问题,请参考以下文章

Luogu 1177 - 模板快速排序 - [快速排序][归并排序][无旋Treap]

九大排序算法及其实现- 插入.冒泡.选择.归并.快速.堆排序.计数.基数.桶排序

算法归并排序

[LeetCode] 3. Longest Substring Without Repeating Characters 最长无重复字符的子串

[SCOI2012]喵星球上的点名——堪称九种方法做的题

最长回文子串