zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    HTTP BIN测试
    JavaMail
    Linux内存分析
    HDU 4118 树形DP Holiday's Accommodation
    线性方程组的求解(C++)
    线性方程组的求解(C++)
    区间树(segment tree)
    区间树(segment tree)
    同余定理在算法求解中的应用
    同余定理在算法求解中的应用
  • 原文地址:https://www.cnblogs.com/WWHHTT/p/9877024.html
Copyright © 2011-2022 走看看