[CF743E]Vladik and cards

题目

传送门

题解

因为所有数字的选择必须连续,也就是说,同一数字的选择越少,其要求越容易达成,并且题目要求任意两个数字的出现次数 \(c(i),c(j)\) 都必须保证 \(\mid c(i)-c(j)\mid \le 1\),我们不妨二分数字出现的最小次数 \(k\)(有少部分数字出现次数为 \(k+1\))然后对这个 \(k\) 进行合法性检查,显然,\(k\) 越大,答案序列越大。

现在问题在于如何进行合法性检查,我们定义 \(f[i][j]\) 为到第 \(i\) 位,数字出现的集合为 \(j\) 时的最大序列长度?

其实有更好的定义方法,定义 \(f[i][j]\) 为到第 \(i\) 位,数字出现的集合为 \(j\) 时,出现次数为 \(k+1\) 的数字最多的出现次数,因为我们已经固定每个数字至少出现 \(k\) 次,那么出现 \(k+1\) 次的数字越多,序列长度必然越大。

考虑如何进行转移:

我们可以将每个数字的出现位置单独储存起来,并用类似于指针的数组记录第 \(i\) 位时,数字 \(num\) 应该从哪个位置开始,因为有些位置在 \(i\) 之前,已经不能选择了,为了快速得到起始位置,我们才这样做,我们记这个数组为 \(cur\)

对于一个数字 \(t\),假设我们已经要选择它,那么显然我们要选择它的连续的 \(k\) 个位置,即起始位置为 \(pos_{cur_{t,i}}\),终止位置为 \(pos_{cur_{t,i}+k-1}\),然后我们检验第 \(i\) 位之后是否还存在 \(k\)\(t\) 即可

然后,便可得到状转

\[f[pos_{cur_{t,i}+k-1}][j']=\max\{f[pos_{cur_{t,i}}][j]\} \]

其中,\(j'\)\(j\) 多出 \(t\) 这个数字的集合

但是这只是一种情况,数字 \(t\) 显然有可能出现 \(k+1\) 次,我们只需再检验第 \(i\) 位之后是否还存在 \(k+1\)\(t\) 之后,再用状转

\[f[pos_{cur_{t,i}+k}][j']=\max\{f[pos_{cur_{t,i}}][j]+1\} \]

注意两个式子中的细微差别,还要些细节可直接见代码

代码

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void read(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T read(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=1000;
const int MAXSIZE=(1<<8);
const int ALL=(1<<8)-1;

int n,a[MAXN+5];

vector<int>pos[10];

inline void Init(){
    n=read(1);
    rep(i,0,n-1)pos[a[i]=read(1)].push_back(i);
}

int cur[10],f[MAXN+5][MAXSIZE+5];
inline int Check(const int cnt){
    memset(cur,0,sizeof cur);
    memset(f,-1,sizeof f);
    f[0][0]=0;
    int x;
    rep(i,0,n-1){
        rep(j,0,ALL)if(f[i][j]!=-1){
            rep(k,1,8){
                if((j>>(k-1))&1)continue;//此数字已经出现过
                x=cur[k]+cnt-1;
                if(x>=pos[k].size())continue;//这个数字根本没那么多
                f[pos[k][x]][j^(1<<(k-1))]=Max(f[pos[k][x]][j^(1<<(k-1))],f[i][j]);
                if((++x)>=pos[k].size())continue;
                f[pos[k][x]][j^(1<<(k-1))]=Max(f[pos[k][x]][j^(1<<(k-1))],f[i][j]+1);
            }
        }++cur[a[i]];//指针自动往前
    }
    int ret=-1;
    rep(i,0,n-1)ret=Max(ret,f[i][ALL]);
    if(ret==-1)return -1;
    return ret*(cnt+1)+(8-ret)*cnt;
}

inline void Bisearch(){
    int l=1,r=n/8,mid;
    while(l+1<r){
        // printf("Now l == %d, r == %d\n",l,r);
        mid=l+r>>1;
        if(Check(mid)!=-1)l=mid;
        else r=mid-1;
    }
    int ans=Max(Check(l),Check(r));
    if(ans==-1){
        ans=0;
        rep(i,1,8)if(pos[i].size())++ans;
    }writc(ans,'\n');
}

signed main(){
    Init();
    Bisearch();
    return 0;
}
posted @ 2020-08-05 21:20  Arextre  阅读(248)  评论(0编辑  收藏  举报