1. 程式人生 > 實用技巧 >[CF743E]Vladik and cards

[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;
}