LOJ#2977. 「THUSCH 2017」巧克力(斯坦纳树+随机化)

题目

题目

做法

  • 考虑部分数据(颜色较少)的:

二分中位数\(mid\),将\(v[i]=1000+(v[i]>mid)\)

具体二分操作:然后求出包含\(K\)种颜色的联通快最小的权值和,判断该权值和是否满足中位数为\(mid\),从而调整范围

其中求权值和显然可以用斯坦纳树解决

  • 正解:

我们每次随机把颜色映射到\([0,K)\)中去,每次得到的结果正确率就为答案联通块的离散颜色正好一一对应的概率:\(\frac{K!}{K^K}\)

随机\(233\)次,有\(99\%\)以上的正确率

Code

#include<bits/stdc++.h>
#include<queue>
typedef int LL;
const LL dx[]={0,-1,0,1,0},dy[]={0,0,1,0,-1};
const LL maxn=7e4+9,inf=1e8;
inline LL Read(){
    LL x=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') f=-1; c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<3)+(x<<1)+c-'0',c=getchar();
    }
    return x*f;
}
std::queue<LL> que;
LL n,m,T,K,C,tot;
LL f[maxn][109],c[maxn],sta[maxn],d[maxn][2],a[maxn],pos[255][255],v[maxn],hs[maxn];
inline bool Ok(LL x,LL y){
    return x>=1 && x<=n && y>=1 && y<=m;
}
inline void Spfa(LL bit){
    for(LL i=1;i<=tot;++i) if(c[i]!=-1) que.push(i);
    while(que.size()){
        LL now(que.front()); que.pop();
        LL x(d[now][0]),y(d[now][1]);
        for(LL i=1;i<=4;++i){
            LL xx(x+dx[i]),yy(y+dy[i]),to(pos[xx][yy]);
            if(!Ok(xx,yy) || c[to]==-1) continue;
            if(f[to][bit]>f[now][bit]+a[to]){
                f[to][bit]=f[now][bit]+a[to];
                que.push(to);
            }
        }
    }
}
inline LL Solve(LL up){
    for(LL i=0;i<up;++i)
        for(LL j=1;j<=tot;++j)
            f[j][i]=inf;
    for(LL i=1;i<=tot;++i) if(c[sta[i]]!=-1) f[i][1<<hs[c[i]]]=a[i];
    for(LL bit=1;bit<up;++bit){
        for(LL i=1;i<=tot;++i){
            LL x(i);
            if(c[x]==-1) continue;
            for(LL bit1=(bit-1)&bit;bit1;bit1=(bit1-1)&bit)
                f[x][bit]=std::min(f[x][bit],f[x][bit1]+f[x][bit-bit1]-a[x]);
        }
        Spfa(bit);
    }
    LL ans(inf);
    for(LL i=1;i<=tot;++i) ans=std::min(ans,f[i][up-1]);
    return ans;
}
int main(){
    srand(time(NULL));
    T=Read();
    while(T--){
        n=Read(); m=Read(); K=Read(); tot=0;
        sta[0]=0;
        C=0;
        for(LL i=1;i<=n;++i)
            for(LL j=1;j<=m;++j){
                pos[i][j]=++tot;
                d[tot][0]=i; d[tot][1]=j;
            }
        for(LL i=1,now=0;i<=n;++i)
            for(LL j=1;j<=m;++j){
                LL col(Read());
                c[++now]=col;//col
                C=std::max(C,col);
            }
        for(LL i=1,now=0;i<=n;++i)
            for(LL j=1;j<=m;++j)
                v[++now]=Read();//val
        LL up(1<<K),ans1(inf),ans2(inf);
        for(LL i=1;i<=233;++i){
            for(LL j=1;j<=C;++j) hs[j]=rand()%K;
            LL l(0),r(1000000),a1(inf),a2(inf);
            while(l<=r){
                LL mid(l+r>>1);
                for(LL i=1;i<=tot;++i) a[i]=(c[i]==-1?inf:1000+(v[i]>mid));
                LL now(Solve(up));
                if(now==inf) break;
                a1=now/1000;
                LL small(a1-(now-a1*1000));
                if(small>=(a1+1>>1)){
                    a2=mid; r=mid-1;
                }else l=mid+1;
            }
            if(a1<ans1 || (a1==ans1 && a2<ans2)){
                ans1=a1; ans2=a2;
            }
        }
        if(ans1==-1) puts("-1 -1");
        else printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

转载于:https://www.cnblogs.com/y2823774827y/p/10832309.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值