20181029 T3 乐谱分段

小 D 是一个乐器爱好者,这一天她在给乐谱分段时遇到了难题。
乐谱是由若干音符组成的,为了方便起见用不同的数字来表示不同的音
符。小 D 想将一个长度为 n 的乐谱 A 分成若干连续的段,要求每一段不能有相
同的音符。
小 D 还想让乐谱的分段尽量平均(即长度最小的一段尽量长)并想知道保
证乐谱长度最小的一段尽量长的条件下有多少种分段的方法。
这么简单的问题小 D 当然会做了,她想考考你,你能不能比她先给出问题
的答案呢?


 第一眼,最小值最大,果断二分答案

然后是DP

先来看二维的怎么写,对于每个点,我们要经过一个循环求出以它结尾它最长的区间的起点的下标

也就是不能有重复,然后求出最短区间要到哪里,把这中间的每一个点的DP值加上,得到的值就是匹配到弹前点的个数

然后我们来想如何优化,首先是最长区间,这个东西我们可以DP求出

转移很简单,dp[i]=max(dp[i-1],st[a[i]]+1)

为什么呢,我们来看,dp[i-1]保证了i前面的不能有重复,st[a[i]]+1保证了a[i]没有重复

而一个区间必须要同时满足这两个条件,所以取max

特别一提,要用离散化

然后是区间和,这个用前缀和来维护

当然,写树状数组也可以

这样就是O(nlogn)的

下面给出代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
inline long long rd(){
    long long x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
long long n;
long long a[1000006],s[1000006];
long long st[1000006];
long long last[1000006];
long long sum[1000006],f[1000006];
long long num[1000006];
long long mod=998244353;
long long check(long long x){
    sum[0]=1;
    for(long long i=1;i<=n;i++){
        long long l=st[i]-1,r=i-x;
        if(l>r) f[i]=0;
        else{
            f[i]=0;
            long long v=0;
            if(l>=1) v=sum[l-1];
            if(sum[r]-v) f[i]=1;
        }
        sum[i]=sum[i-1]+f[i];
    }
    return f[n];
}
int main(){
    n=rd();
    for(long long i=1;i<=n;i++){
        a[i]=rd();
        num[i]=a[i];
    }
    sort(num+1,num+n+1);
    for(long long i=1;i<=n;i++){
        s[i]=lower_bound(num+1,num+n+1,a[i])-num;
    }
    for(long long i=1;i<=n;i++){
        st[i]=max(st[i-1],last[s[i]]+1);
        last[s[i]]=i;
    }
    long long l=1,r=n+1;
    while(l+1<r){
        long long mid=(l+r)>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    write(l),puts("");
    memset(f,0,sizeof(f));
    memset(sum,0,sizeof(sum));
    sum[0]=1;
    long long ans=l;
    for(long long i=1;i<=n;i++){
        long long x=st[i]-1,y=i-ans;
        if(x>y) f[i]=0;
        else{
            f[i]=0;
            long long v=0;
            if(x>=1) v=sum[x-1];
            if(sum[y]-v) f[i]=(sum[y]-v+mod)%mod;
        }
        sum[i]=(sum[i-1]+f[i]+mod)%mod;
    }
    write(f[n]%mod);
    return 0;
}

 

posted @ 2018-10-30 15:21  Bruce--Wang  阅读(377)  评论(0编辑  收藏  举报