题目大意:给你一张图,要求一个包含至少k种颜色的连通块使得在点数最少的前提下中位数最小。k<=5。
题解:首先中位数最小怎么办,可以二分答案,将小的视为M,大的视为M+1,其中M是个比较大的数字,然后要求权值之和最小。
然后可以说明一定存在一种最优解,包含恰好k种颜色(如果有解的话)。
那么我们给每种颜色随即一个[1,k]的权值,那么该最优解包含k种不同的权值概率大约会是
k
!
k
k
\frac{k!}{k^k}
kkk!,大约随机200次正确率就还可以了。
每次随机完了二分,求出权值和可以用斯坦纳树求,所有随机出来的答案取min即可。
斯坦纳树就是状态
d
p
(
x
,
S
)
dp(x,S)
dp(x,S)表示集合
S
S
S当前在
x
x
x,两种转移,先是合并集合
d
p
(
x
,
S
∣
T
)
←
d
p
(
x
,
S
)
×
d
p
(
x
,
T
)
−
v
a
l
u
e
(
x
)
dp(x,S|T)\leftarrow dp(x,S)\times dp(x,T)-value(x)
dp(x,S∣T)←dp(x,S)×dp(x,T)−value(x),然后用一个
s
p
f
a
spfa
spfa求出第二种换根转移
d
p
(
y
,
S
)
←
d
p
(
x
,
S
)
+
w
e
i
g
h
t
(
x
,
y
)
dp(y,S)\leftarrow dp(x,S)+weight(x,y)
dp(y,S)←dp(x,S)+weight(x,y)。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn() { int x;return scanf("%d",&x),x; }
const int INF=1000000,N=250,MXS=(1<<5)+5;const lint MXV=1000000000000;
int a[N][N],b[N][N],c[N][N],inq[N][N],id[N*N];lint v[N][N],dp[MXS][N][N];
queue<pii> q;int dx[5]={0,-1,0,1,0},dy[5]={0,0,1,0,-1},sz[MXS];
struct RAND{
int x;const int p;RAND():p(998244353) { x=1; }
inline int operator()(int a,int b) { return x=(0ll+x+x+x)%p,x%(b-a+1)+a; }
}rnd;
inline int spfa_trans(int n,int m,lint (*dp)[N])
{
while(!q.empty()) q.pop();
rep(i,1,n) rep(j,1,m) if(b[i][j]>=0)
q.push(mp(i,j)),inq[i][j]=1;
while(!q.empty())
{
pii p=q.front();int x=p.fir,y=p.sec;
q.pop();inq[x][y]=0;
rep(i,1,4)
{
int px=x+dx[i],py=y+dy[i];
if(px>0&&px<=n&&py>0&&py<=m&&dp[px][py]>dp[x][y]+v[px][py])
{
dp[px][py]=dp[x][y]+v[px][py];
if(!inq[px][py]) inq[px][py]=1,q.push(mp(px,py));
}
}
}
return 0;
}
inline lint calc(int n,int m,int k,int bas)
{
rep(i,1,n) rep(j,1,m)
if(b[i][j]>=0) v[i][j]=INF+(a[i][j]>bas);
else v[i][j]=MXV;
int all=(1<<k)-1;
rep(i,1,n) rep(j,1,m) rep(t,0,k-1)
if(t==b[i][j]) dp[1<<t][i][j]=v[i][j];
else dp[1<<t][i][j]=MXV;
rep(s,1,all)
{
sz[s]=sz[s>>1]+(s&1);
if(sz[s]>1)
{
rep(i,1,n) rep(j,1,m) dp[s][i][j]=MXV;
for(int t=(s-1)&s;t;t=(t-1)&s) rep(i,1,n) rep(j,1,m)
dp[s][i][j]=min(dp[s][i][j],dp[t][i][j]+dp[s^t][i][j]-v[i][j]);
}
spfa_trans(n,m,dp[s]);
}
lint ans=MXV;
rep(i,1,n) rep(j,1,m) ans=min(ans,dp[all][i][j]);
return ans;
}
inline pii solve(int n,int m,int k,int _R)
{
int L=0,R=_R;rep(i,1,n*m) id[i]=-1;
rep(i,1,n) rep(j,1,m)
{
if(c[i][j]==-1) { b[i][j]=-1;continue; }
if(id[c[i][j]]<0) id[c[i][j]]=rnd(0,k-1);
b[i][j]=id[c[i][j]];
}
if(calc(n,m,k,R)==MXV) return mp(n*m+1,R);
while(L<=R)
{
int mid=(L+R)>>1;lint t=calc(n,m,k,mid);
int cnt=int(t/INF),sml=cnt-(int)(t%INF);
if(sml>=(cnt+1)/2) R=mid-1;else L=mid+1;
}
lint t=calc(n,m,k,L);return mp(int(t/INF),L);
}
int main()
{
for(int T=inn();T;T--)
{
int n=inn(),m=inn(),k=inn(),colcnt=0,R=0;
rep(i,1,n) rep(j,1,m) c[i][j]=inn();
rep(i,1,n) rep(j,1,m) a[i][j]=inn();
rep(i,1,n) rep(j,1,m) R=max(R,a[i][j]);
pii ans=mp(n*m+1,R);
rep(Tms,1,200) ans=min(ans,solve(n,m,k,R));
if(ans.fir>n*m) printf("-1 -1\n");
else printf("%d %d\n",ans.fir,ans.sec);
}
return 0;
}