LOJ 2977 「THUSCH 2017」巧克力

LOJ 2977 「THUSCH 2017」巧克力

神仙题QaQ

做法是给每种颜色随机分配一个$1$到$k$的颜色,然后跑一次斯坦纳树,得到当前包含至少$k$种颜色的最小联通块。

我们考虑什么时候我们可以随机到答案?如果答案的$k$颜色恰好本随机分配到了不同的颜色,那么跑出来的答案肯定就是正确的。所以说,一次随机的正确率大概是$\frac{k!}{k^k}$大概是$0.3\%$。所以我们随机大概 200 次,正确率就远超 100 了。

另外考虑中位数尽量小这个限制,我们二分这个中位数,考虑怎么 check ,当前我们的第一限制是块数,第二限制是拿到的大于中位数的尽可能少。由于我们要让$k$种联通,最多要选的块也不会超过$5 \times (233 + 233)$不会超过 2000 多,实际上还肯定比这个小得多,保险起见(其实是方便)直接把大于中位数设置为$10001$,小于中位数$9999$。然后最后块数就是$\lfloor \frac{ans + 2500}{10000} \rfloor$,这样就可以方便的验证了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "vector"
#include "queue"
using namespace std;
int dirx[4] = { 0,0,-1,1 };
int diry[4] = { 1,-1,0,0 };
#define chkmn( a , b ) ( (a) > (b) ? ( (a) = (b) , 1 ) : 0 )
#define MAXN 256
int n , m , k , s;
int c[MAXN][MAXN] , A[MAXN][MAXN] , w[MAXN][MAXN];
int col[MAXN] , en , rc[MAXN];
int dp[MAXN][MAXN][1<<5];
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
queue<pii> Q;
int vis[MAXN][MAXN];
void spfa( int s ) {
while( !Q.empty() ) {
int x = Q.front().fi , y = Q.front().se; Q.pop();
vis[x][y] = 0;
for( int d = 0 ; d < 4 ; ++ d ) {
int X = x + dirx[d] , Y = y + diry[d];
if( X < 1 || X > n || Y < 1 || Y > m || c[X][Y] == -1 ) continue;
if( chkmn( dp[X][Y][s] , dp[x][y][s] + w[X][Y] ) && !vis[X][Y] )
Q.push( mp( X , Y ) ) , vis[X][Y] = 1;
}
}
}
int work( ) {
int ans = 0x3f3f3f3f;
for( int t = 1 ; t <= 200 ; ++ t ) {
random_shuffle( col + 1 , col + 1 + s );
for( int i = 1 ; i <= s ; ++ i ) rc[col[i]] = i % k;
for( int i = 1 ; i <= n ; ++ i )
for( int j = 1 ; j <= m ; ++ j ) {
for (int st = 0; st < (1 << k); ++st) dp[i][j][st] = 0x3f3f3f3f;
if( ~c[i][j] ) dp[i][j][1<<rc[c[i][j]]] = w[i][j];
}
for( int st = 1 ; st < ( 1 << k ) ; ++ st ) {
for( int i = 1 ; i <= n ; ++ i )
for( int j = 1 ; j <= m ; ++ j ) {
for( int t = ( st - 1 ) & st ; t ; t = ( t - 1 ) & st )
chkmn( dp[i][j][st] , dp[i][j][t] + dp[i][j][st ^ t] - w[i][j] ); // 转移
if( dp[i][j][st] < 1e9 ) Q.push( mp( i , j ) );
}
spfa( st );
}
for( int i = 1 ; i <= n ; ++ i )
for( int j = 1 ; j <= m ; ++ j ) ans = min( ans , dp[i][j][( 1 << k ) - 1]);
}
return ans;
}
int res;
int main() {
srand( 19260817 );
int T;cin >> T;
while( T --> 0 ) {
cin >> n >> m >> k;
for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j )
scanf("%d",&c[i][j]) , col[++ en] = c[i][j];
for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j )
scanf("%d",&A[i][j]);
sort( col + 1 , col + 1 + en );
s = unique( col + 1 , col + 1 + en ) - col - 1;
for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j ) w[i][j] = 1;
res = work( );
if( res > 1e9 ) { puts("-1 -1"); continue; }
int l = 0 , r = 1e6;
while( l <= r ) {
int mid = l + r >> 1;
for( int i = 1 ; i <= n ; ++ i ) for( int j = 1 ; j <= m ; ++ j )
w[i][j] = ( A[i][j] <= mid ? 9999 : 10001 );
int re = work( );
if( re <= ( re + 2500 ) / 10000 * 10000 ) r = mid - 1;
else l = mid + 1;
}
printf("%d %d\n",res,l);
}
}
文章作者: yijan
文章链接: https://yijan.co/old75/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog