题目大意

  有一个网格(或者你可以认为这是一个图),每个点都有颜色 \(c_i\) 和点权 \(a_i\)。

  求最小的连通块,满足这个连通块内点的颜色数量 \(\geq k\)。在满足点数最少的前提下,要求点权的中位数最少。

  \(n\leq 233,c_i\leq n,k\leq 5\)

题解

  如果 \(c_i\) 很小,就可以直接用斯坦纳树做。

  本题要求在满足点数最少的前提下,要求点权的中位数最少。那么可以二分中位数 \(s\),将 \(a_i\leq s\) 的点的权值设为 \(M-1\),\(a_i>s\) 的点的权值设为 \(M+1\),其中 \(M\) 是一个很大的数。这样就可以在满足点数最小的前提下求出最小中位数是否 \(\leq s\)。

  还有一个问题是 \(c_i\) 比较大。我们可以把每一种颜色随机映射到 \([1,k]\) 中,当答案的 \(k\) 种颜色映射到 \([1,k]\) 中不同的值的时候就能求出正确答案。求一次正确的概率是 \(\frac{k!}{k^k}\),多求几次就好了。

  时间复杂度:\(O(T(3^kn+2^kn\log n)\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int inf=0x3fffffff;
const int N=300;
int _n,_m;
int n,k;
int t;
int a[N],c[N],d[N],e[N],b[N],vis[N];
int a2[N],c2[N];
int f[1<<5][N];
vector<int> g[N];
pii ans;
queue<pii> q1,q2,q3;
//priority_queue<pii,vector<pii>,greater<pii> > q;
int id(int x,int y)
{
return (x-1)*_m+y;
}
vector<pii> h;
int check(int v)
{
for(int i=1;i<=n;i++)
if(a[i]<=v)
a2[i]=100000-1;
else
a2[i]=100000+1;
for(int i=0;i<1<<k;i++)
for(int j=1;j<=n;j++)
f[i][j]=inf;
for(int i=1;i<=n;i++)
if(c2[i]!=-1)
f[1<<(c2[i]-1)][i]=0;
for(int i=1;i<1<<k;i++)
{
for(int j=(i-1)&i;j;j=(j-1)&i)
for(int l=1;l<=n;l++)
f[i][l]=min(f[i][l],f[j][l]+f[i^j][l]);
h.clear();
for(int j=1;j<=n;j++)
{
h.push_back(pii(f[i][j],j));
vis[j]=0;
}
sort(h.begin(),h.end());
for(int i=0;i<n;i++)
q3.push(h[i]);
while(!q1.empty()||!q2.empty()||!q3.empty())
{
pii x;
if(!q1.empty()&&(q2.empty()||(q1.front()<=q2.front()))&&(q3.empty()||(q1.front()<=q3.front())))
{
x=q1.front();
q1.pop();
}
else if(!q2.empty()&&(q1.empty()||(q2.front()<=q1.front()))&&(q3.empty()||(q2.front()<=q3.front())))
{
x=q2.front();
q2.pop();
}
else
{
x=q3.front();
q3.pop();
}
if(vis[x.second])
continue;
vis[x.second]=1;
f[i][x.second]=min(f[i][x.second],x.first);
x.first+=a2[x.second];
// for(auto v:g[x.second])
for(vector<int>::iterator it=g[x.second].begin();it!=g[x.second].end();it++)
{
int v=*it;
if(!vis[v])
{
if(a2[x.second]==100000-1)
q1.push(pii(x.first,v));
else
q2.push(pii(x.first,v));
}
}
}
}
int res=inf;
for(int i=1;i<=n;i++)
if(c2[i]!=-1)
{
f[(1<<k)-1][i]+=a2[i];
res=min(res,f[(1<<k)-1][i]);
}
return res;
}
void gao()
{
for(int i=1;i<=n;i++)
b[i]=rand()%k+1;
for(int i=1;i<=n;i++)
if(c[i]!=-1)
c2[i]=b[c[i]];
else
c2[i]=-1;
int l=1,r=t;
int temp=check(1);
if(temp==inf)
return;
int v=(int)round((db)temp/100000);
if(v>ans.first)
return;
while(l<r)
{
int mid=(l+r)>>1;
temp=check(mid);
if(temp<=(int)round((db)temp/100000)*100000)
r=mid;
else
l=mid+1;
}
ans=min(ans,pii(v,l));
}
void solve()
{
scanf("%d%d%d",&_n,&_m,&k);
n=_n*_m;
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
d[i]=a[i];
}
sort(d+1,d+n+1);
t=unique(d+1,d+n+1)-d-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(d+1,d+t+1,a[i])-d;
for(int i=1;i<=n;i++)
g[i].clear();
for(int i=1;i<=_n;i++)
for(int j=1;j<=_m;j++)
if(c[id(i,j)]!=-1)
{
if(i>1&&c[id(i-1,j)]!=-1)
g[id(i,j)].push_back(id(i-1,j));
if(i<_n&&c[id(i+1,j)]!=-1)
g[id(i,j)].push_back(id(i+1,j));
if(j>1&&c[id(i,j-1)]!=-1)
g[id(i,j)].push_back(id(i,j-1));
if(j<_m&&c[id(i,j+1)]!=-1)
g[id(i,j)].push_back(id(i,j+1));
}
ans=pii(inf,inf);
for(int times=200;times;times--)
gao();
if(ans.first==inf)
printf("-1 -1\n");
else
printf("%d %d\n",ans.first,d[ans.second]);
}
int main()
{
srand(19260817);
open("chocolate");
int t;
scanf("%d",&t);
while(t--)
solve();
return 0;
}z

【THUSC2017】【LOJ2977】巧克力 斯坦纳树的更多相关文章

  1. loj2977 巧克力 (斯坦纳树+随机化)

    考虑颜色比较少的时候,第一问可以直接斯坦纳树 第二问考虑二分,每次把每格的权值给成1000+[a[i]>m],就是在个数最少的基础上尽量选小于等于m的 然而颜色太多不能直接做,但可以把每种颜色映 ...

  2. [THUSC2017]巧克力[斯坦纳树、随机化]

    题意 题目链接 分析 对于第一问,如果颜色数量比较少的话可以 \(\binom{cnt}{k}\) 枚举最终连通块中的 \(k\) 种颜色,然后利用斯坦纳树求解. 如果颜色比较多,考虑将所有的颜色重新 ...

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

    题目 题目 做法 考虑部分数据(颜色较少)的: 二分中位数\(mid\),将\(v[i]=1000+(v[i]>mid)\) 具体二分操作:然后求出包含\(K\)种颜色的联通快最小的权值和,判断 ...

  4. LOJ 2997 「THUSCH 2017」巧克力——思路+随机化+斯坦纳树

    题目:https://loj.ac/problem/2977 想到斯坦纳树.但以为只能做 “包含一些点” 而不是 “包含一些颜色” .而且不太会处理中位数. 其实 “包含一些颜色” 用斯坦纳树做也和普 ...

  5. 洛谷 P7450 - [THUSCH2017] 巧克力(斯坦纳树+随机化)

    洛谷题面传送门 9.13 补之前 8.23 做的题,不愧是鸽子 tzc( 首先我们先来探讨一下如果 \(c_{i,j}\le k\) 怎么做,先考虑第一问.显然一个连通块符合条件当且仅当它能够包含所有 ...

  6. 【BZOJ2595】游览计划(状压DP,斯坦纳树)

    题意:见题面(我发现自己真是越来越懒了) 有N*M的矩阵,每个格子有一个值a[i,j] 现要求将其中的K个点(称为关键点)用格子连接起来,取(i,j)的费用就是a[i,j] 求K点全部连通的最小花费以 ...

  7. HDU 4085 斯坦纳树

    题目大意: 给定无向图,让前k个点都能到达后k个点(保护地)中的一个,而且前k个点每个需要占据后k个中的一个,相互不冲突 找到实现这个条件达到的选择边的最小总权值 这里很容易看出,最后选到的边不保证整 ...

  8. hdu4085 Peach Blossom Spring 斯坦纳树,状态dp

    (1)集合中元素表示(1<<i), i从0开始 (2)注意dp[i][ss] = min(dp[i][ss], dp[i][rr | s[i]] + dp[i][(ss ^ rr) | s ...

  9. hdu 3311 斯坦纳树

    思路:虚拟一个0号节点,将每个点建一条到0号节点的边,权值为挖井需要的价值.并要保证0号节点同另外n个寺庙一样被选择即可. 然后就是求斯坦纳树了. #include<map> #inclu ...

随机推荐

  1. Docker在Linux上运行NetCore系列(三)在Linux上使用Docker运行Asp.NetCore

    转发请注明此文章作者与路径,请尊重原著,违者必究. 系列文章:https://www.cnblogs.com/alunchen/p/10121379.html 开始说明 上几篇文章都是通过Linux运 ...

  2. 高并发系统保护~ing

    由于公司业务发展,需要考虑一些高并发系统保护的问题,整理记录一下. 当发现你的系统出现访问卡顿,服务器各种性能指标接近100%(如果一个初创型企业系统正常运行情况下出现这个问题,那么应该恭喜你,你懂得 ...

  3. Spring中关于AOP的实践之概念

    一.什么是AOP AOP:也称作面向切面编程 在分享几个概念执行我想先举个栗子(可能例子举得并不是特别恰当): 1.假如路人A走在大街上,被一群坏人绑架了: 2.警察叔叔接到报警迅速展开行动:收集情报 ...

  4. 理清PHP在Linxu下执行时的文件权限

    首先推荐一个linux权限的视频:Linux权限管理之基本权限,讲的非常好,看完后就基本明白了. 一.文件权限及所属 1.文件有三种类型的权限,为了方便期间,可以用数字来代替,这样可以通过数字的加减, ...

  5. 好代码是管出来的——C#的代码规范

    代码是软件开发过程的产物,代码的作用是通过编译器编译后运行,达到预期的效果(功能.稳定性.安全性等等),而另外一个重要作用是给人阅读.对于机器来说只要代码正确就能够正确的运行程序,但是人不同,如果代码 ...

  6. django连接sqlserver

    http://www.cnblogs.com/yijiaming/p/9684601.html 方法一: 1.需要安装pymssql pip install pymssql 2.使用方法: impor ...

  7. Failure sending mail: The user or group name 'xxx\xxxx' is not recognized.Mail will not be resent

      一批Reporting Service报表的订阅突然报如下错误: Failure sending mail: The user or group name 'xxx\xxxx' is not re ...

  8. 运算符和Scanner的使用

    一 1.运算符:对常量或者变量进行操作的符号 2.算术运算符: 1)四则运算:+ - * / 2)取模运算:%(只看余数) 3)字符串连接:任何数据类型和字符串连在一起,都是字符串结果(+来连接) 4 ...

  9. python 操作Excel文件

    1   安装xlrd.xlwt.xlutils cmd下输入: pip install xlrd        #读取excel pip install xlwt        #写入excel pi ...

  10. 查询本地电脑IP地址

    使用Windows+R键打开"运行"窗口,然后输入CMD进入命令提示窗口 进入命令窗口之后,输入:ipconfig/all 回车即可看到整个电脑的详细的IP配置信息