Codeforces Raif Round 1 (Div. 1 + Div. 2) F. Fruit Sequences(dp)

题意:

给出一个长度为 n n n 01 01 01串,定义 f ( l , r ) f(l,r) f(l,r)为区间 [ l , r ] [l,r] [l,r] 的贡献为区间内最长连续 1 1 1的长度,求所有区间的贡献和。

题解:

s u f [ i ] suf[i] suf[i]表示 i i i 位置后面连续的 1 1 1的长度,用 p r e [ i ] pre[i] pre[i]表示 i i i 位置前面连续的 1 1 1的长度。

枚举区间右端点 r r r,当 l l l ≥ \geq r + p r e [ r ] − 1 r+pre[r]-1 r+pre[r]1 时,可以利用前缀和直接算出答案。

l l l < < < r + p r e [ r ] − 1 r+pre[r]-1 r+pre[r]1 时 ,找到最近的一个位置 j j j 使得 s u f [ j ] > p r e [ r ] suf[j]>pre[r] suf[j]>pre[r]

l > j l>j l>j 时,贡献依旧是 p r e [ r ] pre[r] pre[r]

l ≤ j l \leq j lj 时,那么贡献就不在是 p r e [ r ] pre[r] pre[r] 。怎么求呢?

d p [ i ] = ∑ j = 1 i f ( j , i + s u f [ i ] − 1 ) dp[i]= \sum\limits_{j=1}^{i} f(j,i+suf[i]-1) dp[i]=j=1if(j,i+suf[i]1)。当 j ≥ i + p r e [ i ] − 1 j \geq i+pre[i]-1 ji+pre[i]1 时,可直接算出答案加到 d p [ i ] dp[i] dp[i] 中 。否则,找到最近的位置 k使得 s u f [ k ] > s u f [ i ] + p r e [ i ] − 1 suf[k] > suf[i]+pre[i]-1 suf[k]>suf[i]+pre[i]1 。当 j > k j>k j>k 时,答案还是 s u f [ i ] + p r e [ i ] − 1 suf[i]+pre[i]-1 suf[i]+pre[i]1 ,否则就是 d p [ k ] dp[k] dp[k]

预处理出 d p dp dp后,那么当 l ≤ j l \leq j lj 时 ,贡献就是 d p [ j ] dp[j] dp[j]

具体怎么找最近的 j j j k k k 呢?用 R M Q RMQ RMQ 预处理出区间 s u f [ i ] suf[i] suf[i] 最值,然后就可以二分查找。

代码:

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=5e5+5;
const int inf=0x3f3f3f3f;
char s[MAXN];
int suf[MAXN],pre[MAXN];
int dpma[MAXN][60], dpmi[MAXN][60],a[MAXN];
ll dp[MAXN];
ll sum[MAXN];
void RMQ(int n)
{
    for(int i = 1; i <= n; i++){
        dpmi[i][0] = suf[i], dpma[i][0] = suf[i];
    }
    for(int j = 1; (1 << j) <= n; j++){
        for(int i = 1; i + (1 << j) - 1 <= n; i++){
            dpma[i][j] = max(dpma[i][j - 1], dpma[i + (1 << (j - 1))][j - 1]);
            dpmi[i][j] = min(dpmi[i][j - 1], dpmi[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int QueryMax(int l, int r){
    int k = log2(r - l + 1);
    return max(dpma[l][k], dpma[r - (1 << k) + 1][k]);
}
int QueryMin(int l, int r){
    int k = log2(r - l + 1);
    return min(dpmi[l][k], dpmi[r - (1 << k) + 1][k]);
}
int check(int en,int x)
{
    int l=1,r=en;
    int pos=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(QueryMax(mid,en)>=x)
        {
            pos=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    return pos;
}
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        a[i]=s[i]-'0';
    }
    for(int i=1;i<=n;i++)
    {
        if(a[i]==1) pre[i]=pre[i-1]+1;
    }
    for(int i=n;i>=1;i--)
    {
        if(a[i]==1) suf[i]=suf[i+1]+1;
    }
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+suf[i];
    RMQ(n);
    for(int i=1;i<=n;i++)
    {
        if(a[i]==0)
        {
            dp[i]=dp[i-1];
            continue;
        }      
        int p=i-pre[i]+1;
        dp[i]=sum[i]-sum[p-1];
        int x=pre[i]+suf[i]-1;
        int pos=check(p-1,x+1);
        dp[i]+=dp[pos]+1ll*(p-1-pos)*x;
    }
    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+pre[i];
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        int p=i-pre[i]+1;
        if(p<=i) ans+=sum[i]-sum[p-1];
        int pos=check(p-1,pre[i]+1);
        ans=ans+dp[pos]+1ll*(p-1-pos)*pre[i];
    }
    cout<<ans<<endl;
}
posted @ 2021-04-13 22:16  TheBestQAQ  阅读(24)  评论(0)    收藏  举报