码迷,mamicode.com
首页 > 其他好文 > 详细

CodeChef STRSUB(dp+二分)

时间:2015-03-09 20:58:44      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:codechef   strsub   

题意:(中文题意)

https://codechef_shared.s3.amazonaws.com/download/translated/MARCH15/mandarin/STRSUB.pdf

解析:

先预处理一个数组pre[],pre[i]表示i这个位置,往前最多能找到哪个位置是满足0和1都不大于k的。
然后以每个位置i为左区间的长度就可以计算出,为 (r - pre[i] + 1)

利用这个在预处理一个前缀和数组dp[]。
dp[i]表示:从1到i有多少个子串是满足的题目所给的条件。
注意:pre[]这个数组肯定是递增的 这个很容易证明。

dp[i] = dp[i-1] + (i - pre[i] + 1);
(i - pre[i] + 1)为左区间的长度

然后每次询问一个区间[l, r]
利用二分在pre[]数组找到一个位置pos,表示pos到r之间的位置的pre[i]都是大于l的,其中pre[]是一个单调递增的序列,这个可以证明。

那么这部分的个数就可以计算出来,为dp[r] - dp[pos - 1]。
到了这里你可能会问,为什么不直接计算 dp[r] - dp[l-1]
因为 l~pos 中dp[i]可能会把 小于 l 的部分也计算进去,从而多计算。

那么对于l到pos之间的个数, 由于这部分的pre全都是大于l的,
所以这部分每个位置的个数就是 (i - l + 1)
那么这部分的计算方式就是一个等差数列前n项目和。
1 + 2 + 3 + 4 + … + pos - l

等差数列可以预处理出来 或者利用公式直接计算
两部分和加起来就是每次询问的答案了

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int pre[N];
char str[N];

ll S[N], dp[N];
void getS() {
    S[0] = 0;
    for(int i = 1; i < N; i++)
        S[i] = S[i-1] + i;
}
//二分求出第一个满足 pre[pos] 大于L的位置
int find(int L, int R) {
    int lo = L;
    R++;
    while(L < R) {
        int M = L + (R-L)/2;
        if(pre[M] < lo) L = M+1;
        else R = M;
    }
    return L;
}
int main() {
    getS();
    int T;
    int n, k, q;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d", &n, &k, &q);
        scanf("%s", str+1);

        int one = 0, zero = 0, left = n+1;
        for(int i = n; i >= 1; i--) { //求出i的左区间
            while(one <= k && zero <= k && left > 0) {
                left--;
                if(str[left] == ‘0‘) zero++; else one++;
            }
            pre[i] = left+1;
            if(str[i] == ‘0‘) zero--; else one--;
        }

        //dp[i]表示到当前i满足的所有子区间的个数
        dp[0] = 0;
        for(int i = 1; i <= n; i++)
            dp[i] = dp[i-1] + (i-pre[i]+1);

        int L, R;
        while(q--) {
            scanf("%d%d", &L, &R);
            int pos = find(L, R);
            ll ans = dp[R] - dp[pos-1];
            ans += S[pos-L];
            printf("%lld\n", ans);
        }
    }
    return 0;
}

CodeChef STRSUB(dp+二分)

标签:codechef   strsub   

原文地址:http://blog.csdn.net/helloworld10086/article/details/44158675

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!