CF95E Lucky Country

发布时间 2023-09-08 10:34:00作者: NBest

2023-07-26 14:35:10 solution

思路

第一眼看以为是个图论,结果发现缩点之后就是个多重背包裸题。

我们把原连通块变成一个重量为连通块大小的物体,然后发现只需要找在容量为幸运数的情况下,放满容量所选物体数量的最小值。

考虑背包,但是复杂度为 \(O(n^2)\),观察到这题有根号性质,也就是把重量相同的物体缩在一起后总数量不会超过 \(\sqrt n\),然后做一个二进制优化对多重背包即可,总复杂度 \(O(n\sqrt n \log n)\)

\(Code\)

unordered_map<int,int>M;
int n,m,fa[100005],siz[100005],w[100005],v[100005],tot,f[100005];
bool  pd[100005];
inline int find(int x){
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
inline void merge(int x,int y){
    int xx=find(x),yy=find(y);
    if(xx==yy)return;
    if(siz[xx]>siz[yy])xx^=yy^=xx^=yy;
    siz[yy]+=siz[xx],siz[xx]=0;
    fa[xx]=yy;
}
bool is(int x){
    while(x){
        if(x%10!=4&&x%10!=7)return 0;
        x/=10;
    }
    return 1;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        merge(u,v);
    }
    for(int i=1;i<=n;i++){
        if(pd[find(i)])continue;
        pd[fa[i]]=1;
        M[siz[fa[i]]]++;
    }
    for(auto PI:M){
        int W=PI.first,S=PI.second;
        for(int k=__lg(S)-1;S&&~k;--k){
            if(S>=(1<<k))w[++tot]=W*(1<<k),v[tot]=(1<<k),S-=(1<<k);
        }
        if(S)w[++tot]=W*S,v[tot]=S;
    }
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=tot;i++){
        for(int j=n;j>=w[i];--j){
            f[j]=min(f[j],f[j-w[i]]+v[i]);
        }
    }
    int ans=1e9;
    for(int i=4;i<=n;i++){
        if(is(i)){
            ans=min(ans,f[i]-1);
        }
    }
    if(ans>n)ans=-1;
    cout<<ans;
}

后话

所以为什么这题评紫,多重背包是绿题唉。一开始看到这题是紫都不敢用背包,看见背包才敢想背包正解。