2022-2023 ICPC East Central North America Regional Contest (ECNA 2022)
Preface
闲了两天没训练,今天又开始上班,结果唐得发昏后期也没题可写直接光速下班
只能说感觉老外的题目难度跨度都好大,easy确实简单,hard确实难,medium确实少
A. A-Mazing Puzzle
题目看起来很复杂,但仔细一想会发现有用的状态总数只有\(4n^2\)种
即我们可以暴力记录下两个机器人的坐标,然后记下其中一个机器人的方向,就可以唯一确定一个状态了(因为两个机器人的方向的相对差距是不变的,因此记一个就行)
然后转移的话就直接写一个类似Dijkstra的东西即可,要注意各种细节
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
#define ft first
#define sd second
const int N = 55;
const int dir1[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
const int dir2[4][3] = {{0, 0, 0}, {1, 0, 0}, {0, 0, -1}, {1, -1, 0}};
const char pat[5] = "NESW";
int n, c, r, e;
int c1, r1, d1, c2, r2, d2;
bool wall[2][55][55];
int dis[52][52][52][52][2];
bool inD[52][52][52][52];
struct Node{
int cc1, rr1, cc2, rr2;
pii d;
bool operator<(const Node &b)const{return d > b.d;}
};
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
memset(dis, 0x3f, sizeof(dis));
char ch1, ch2;
cin >> c >> r >> e;
cin >> c1 >> r1 >> ch1 >> c2 >> r2 >> ch2;
for (int i=0; i<4; ++i){
if (ch1==pat[i]) d1=i;
if (ch2==pat[i]) d2=i;
}
cin >> n;
for (int i=1; i<=n; ++i){
int c0, r0; cin >> c0 >> r0;
wall[0][c0][r0]=true;
}
for (int i=1; i<=c; ++i) wall[0][i][0]=wall[0][i][r]=true;
for (int i=1; i<=r; ++i) wall[1][0][i]=wall[1][c][i]=true;
wall[0][e][0]=false;
cin >> n;
for (int i=1; i<=n; ++i){
int c0, r0; cin >> c0 >> r0;
wall[1][c0][r0]=true;
}
priority_queue<Node> que;
que.push(Node{c1, r1, c2, r2, make_pair(0, 0)});
dis[c1][r1][c2][r2][0]=0, dis[c1][r1][c2][r2][1]=0;
while (!que.empty()){
auto [cc1, rr1, cc2, rr2, pp] = que.top(); que.pop();
// printf("cc1=%d rr1=%d cc2=%d rr2=%d pp(%d %d)\n", cc1, rr1, cc2, rr2, pp.ft, pp.sd);
if (inD[cc1][rr1][cc2][rr2]) continue;
inD[cc1][rr1][cc2][rr2]=true;
for (int j=0; j<4; ++j){
int bp=0;
int nc1, nr1;
auto [a1, b1, c1] = dir2[(j+d1)%4];
if (wall[a1][cc1+b1][rr1+c1]) nc1=cc1, nr1=rr1, ++bp;
else if (cc1==e && rr1==0) nc1=cc1, nr1=rr1;
else nc1=cc1+dir1[(j+d1)%4][0], nr1=rr1+dir1[(j+d1)%4][1];
int nc2, nr2;
auto [a2, b2, c2] = dir2[(j+d2)%4];
if (wall[a2][cc2+b2][rr2+c2]) nc2=cc2, nr2=rr2, ++bp;
else if (cc2==e && rr2==0) nc2=cc2, nr2=rr2;
else nc2=cc2+dir1[(j+d2)%4][0], nr2=rr2+dir1[(j+d2)%4][1];
if (bp==2) continue;
if (nc1==nc2 && nr1==nr2 && !(nc1==e && nr1==0)) continue;
pii npp = make_pair(pp.ft+1, pp.sd+bp);
// printf("nc1=%d nr1=%d nc2=%d nr2=%d, npp(%d %d)\n", nc1, nr1, nc2, nr2, npp.ft, npp.sd);
if (npp < make_pair(dis[nc1][nr1][nc2][nr2][0], dis[nc1][nr1][nc2][nr2][1])){
que.push(Node{nc1, nr1, nc2, nr2, npp});
dis[nc1][nr1][nc2][nr2][0]=npp.ft;
dis[nc1][nr1][nc2][nr2][1]=npp.sd;
// if ((nc1==e && nr1==0) && (nc2==e && nr2==0)){
// printf("FUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
// }
}
}
// puts("");
}
cout << dis[e][0][e][0][0] << ' ' << dis[e][0][e][0][1] << '\n';
return 0;
}
B. A Musical Question
签到,直接暴力DP即可,二元组\((x,y)\)表示第一张唱片刻录长度为\(x\)的歌,第二张唱片刻录长度为\(y\)的歌的状态
#include<cstdio>
#include<iostream>
#include<vector>
#include<utility>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=1005;
int n,m; bool vis[N][N];
int main()
{
RI i; vector <pi> S; S.push_back(pi(0,0)); vis[0][0]=1;
for (scanf("%d%d",&m,&n),i=1;i<=n;++i)
{
int w; scanf("%d",&w); vector <pi> NS;
for (auto [x,y]:S)
{
if (x+w<=m&&!vis[x+w][y]) vis[x+w][y]=1,NS.push_back(pi(x+w,y));
if (y+w<=m&&!vis[x][y+w]) vis[x][y+w]=1,NS.push_back(pi(x,y+w));
}
for (auto it:NS) S.push_back(it);
}
int ans1=0,ans2=0; for (auto [x,y]:S)
if (x+y>ans1+ans2||(x+y==ans1+ans2&&abs(x-y)<abs(ans1-ans2))) ans1=x,ans2=y;
return printf("%d %d",max(ans1,ans2),min(ans1,ans2)),0;
}
C. Cribbage On Steroids
首先pair
的贡献很好算,然后顺子的话只要枚举顺子的起点和终点即可快速算贡献
至于15's的话可以直接大力DP,设\(f_{i,j}\)表示用前\(i\)种点数的牌组成价值和为\(j\)的牌面的方案数,转移很trivial
#include<bits/stdc++.h>
using namespace std;
#define int long long
int C[105][105];
int n, cnt[20];
int sum[20];
int f[20][20];
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
C[0][0]=C[1][0]=C[1][1]=1;
for (int i=2; i<105; ++i){
C[i][0]=C[i][i]=1;
for (int j=1; j<i; ++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
}
// for (int i=0; i<=100; ++i) printf("C[100][%d]=%lld\n", i, C[100][i]);
cin >> n;
for (int i=1; i<=n; ++i){
char ch; cin >> ch;
if ('T'==ch) ++cnt[10];
else if ('J'==ch) ++cnt[11];
else if ('Q'==ch) ++cnt[12];
else if ('K'==ch) ++cnt[13];
else if ('A'==ch) ++cnt[1];
else ++cnt[ch-'0'];
}
int ans=0;
for (int i=1; i<=13; ++i){
sum[i] += sum[i-1]+i*cnt[i];
}
for (int i=1; i<=13; ++i){
if (cnt[i]>=2) ans += cnt[i]*(cnt[i]-1);
}
// printf("ans=%lld\n", ans);
int lst=0, res=1;
for (int i=1; i<=14; ++i){
if (0==cnt[i]){
if (i-lst-1>=3) ans += res*(i-lst-1);
res=1; lst=i;
}else res*=cnt[i];
}
// printf("ans=%lld\n", ans);
cnt[10]+=cnt[11];
cnt[10]+=cnt[12];
cnt[10]+=cnt[13];
f[0][0]=1;
for (int i=1; i<=10; ++i){
for (int j=0; j<=15; ++j){
for (int k=0; k<i; ++k){
for (int w=1; w<=cnt[i]; ++w){
if (w*i>j) break;
f[i][j] += f[k][j-w*i]*C[cnt[i]][w];
}
}
}
}
for (int i=1; i<=10; ++i) ans += f[i][15]*2;
cout << ans << '\n';
return 0;
}
D. Determining Nucleotide Assortments
签到,注意不要搞错ATGC
的顺序
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+5;
using pii = pair<int, int>;
#define ft first
#define sd second
int n, q;
int cnt[4][N];
char pat[5] = "ATGC";
char str[N];
signed main(){
scanf("%s", str+1);
n = strlen(str+1);
for (int i=1; i<=n; ++i){
for (int j=0; j<4; ++j){
if (str[i]==pat[j]) ++cnt[j][i];
}
}
for (int i=1; i<=n; ++i){
for (int j=0; j<4; ++j){
cnt[j][i] += cnt[j][i-1];
}
}
scanf("%d", &q);
while (q--){
int l, r; scanf("%d%d", &l, &r);
vector<pii> vec;
for (int j=0; j<4; ++j) vec.push_back(make_pair(cnt[j][r]-cnt[j][l-1], j));
sort(vec.begin(), vec.end(), [&](pii a, pii b){return a.ft!=b.ft ? a.ft>b.ft : a.sd<b.sd;});
for (auto [a, b] : vec) putchar(pat[b]);
puts("");
}
return 0;
}
E. Hilbert's Hedge Maze
因为前面我太唐了没给徐神留出足够的时间,红豆泥私密马赛
这题徐神给我讲了做法我也没太懂,只能说笨比脑子是这样的,只能坐等徐神补题了
Upt:徐神光速补完了这题,ORZ
#include <bits/stdc++.h>
using llsi = long long signed int;
struct vivi {
llsi x, y;
inline vivi operator +(const vivi &b) const { return {x + b.x, y + b.y}; }
inline vivi operator -(const vivi &b) const { return {x - b.x, y - b.y}; }
inline vivi rotl(const vivi &b) const {
return {b.x - (y - b.y), b.y + (x - b.x)};
}
inline vivi rotr(const vivi &b) const {
return {b.x + (y - b.y), b.y - (x - b.x)};
}
inline llsi dis(const vivi &b) const {
return std::abs(x - b.x) + std::abs(y - b.y);
}
inline bool operator ==(const vivi &b) const {
return x == b.x && y == b.y;
}
};
struct msg {
llsi n; // l, r, d, t;
vivi from, to, hibiki;
};
vivi hikari(llsi n, vivi pos, /*llsi l, llsi r, llsi d, llsi t,*/ std::vector<msg> &st) {
// std::cerr << "hikari(" << n << ", (" << pos.x << ", " << pos.y << "), " /*<< l << ", " << r << ", " << d << ", " << t << ", "*/ << "[std::vector<msg>])\n";
if(pos.x <= 0ll || pos.x >= (1ll << n) || pos.y <= 0ll || pos.y >= (1ll << n)) return pos;
if(n == 1) {
st.push_back(msg{n, /*l, r, d, t,*/ pos, pos - vivi{0, 1ll}, pos});
return pos - vivi{0, 1ll};
}
/* llsi xmid = (l + r) >> 1ll, ymid = (d + t) >> 1ll; */
vivi from, to, hibiki;
const vivi R = {1ll << n - 2, 1ll << n - 2};
if(pos.x == (1ll << n - 1) || pos.y == (1ll << n - 1)) from = pos;
if(pos.x < (1ll << n - 1) && pos.y < (1ll << n - 1)) from = hikari(n - 1, pos.rotl(R) , /* l, xmid , d, ymid ,*/ st).rotr(R);
if(pos.x > (1ll << n - 1) && pos.y < (1ll << n - 1)) from = hikari(n - 1, (pos - vivi{1ll << n - 1, 0ll}).rotr(R) , /*xmid + 1, r, d, ymid ,*/ st).rotl(R) + vivi{1ll << n - 1, 0ll};
if(pos.x < (1ll << n - 1) && pos.y > (1ll << n - 1)) from = hikari(n - 1, pos - vivi{0ll , 1ll << n - 1} , /* l, xmid , ymid + 1, t,*/ st) + vivi{0ll , 1ll << n - 1};
if(pos.x > (1ll << n - 1) && pos.y > (1ll << n - 1)) from = hikari(n - 1, pos - vivi{1ll << n - 1, 1ll << n - 1} , /*xmid + 1, r, ymid + 1, t,*/ st) + vivi{1ll << n - 1, 1ll << n - 1};
if(pos.x < (1ll << n - 1) && pos.y < (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from.rotr(R) , to = to.rotr(R) , hibiki = hibiki.rotr(R) ;
if(pos.x > (1ll << n - 1) && pos.y < (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from.rotl(R) + vivi{1ll << n - 1, 0ll} , to = to.rotl(R) + vivi{1ll << n - 1, 0ll} , hibiki = hibiki.rotl(R) + vivi{1ll << n - 1, 0ll} ;
if(pos.x < (1ll << n - 1) && pos.y > (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from + vivi{0ll , 1ll << n - 1}, to = to + vivi{0ll , 1ll << n - 1}, hibiki = hibiki + vivi{0ll , 1ll << n - 1};
if(pos.x > (1ll << n - 1) && pos.y > (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from + vivi{1ll << n - 1 , 1ll << n - 1}, to = to + vivi{1ll << n - 1 , 1ll << n - 1}, hibiki = hibiki + vivi{1ll << n - 1 , 1ll << n - 1};
// std::cerr << "from = (" << from.x << ", " << from.y << ")\n";
if(from.x <= 0ll || from.x >= (1ll << n) || from.y <= 0ll || from.y >= (1ll << n)) return from;
if(from.y <= (1ll << n - 1)) to = vivi{1ll << (n - 1), 0} , hibiki = vivi{1ll << (n - 1), 1ll};
else to = vivi{1ll << (n - 1), 1ll << n}, hibiki = vivi{1ll << (n - 1), (1ll << n) - 1};
// std::cerr << "to = (" << to.x << ", " << to.y << ")\n";
st.push_back(msg{n, /*l, r, d, t,*/ from, to, hibiki});
return to;
}
int main(void) {
std::ios::sync_with_stdio(false);
int t; std::cin >> t; while(t--) {
llsi n; vivi A, B;
std::cin >> n >> A.x >> A.y >> B.x >> B.y;
A = A + vivi{1ll, 1ll}; B = B + vivi{1ll, 1ll};
std::vector<msg> as, bs;
A = hikari(n, A, /*1, 1ll << n, 1, 1ll << n,*/ as);
// std::cerr << "debug as: \n";
// for(auto [n, from, to, hibiki]: as) std::cerr << n << " (" << from.x << ", " << from.y << "), (" << hibiki.x << ", " << hibiki.y << ")\n";
B = hikari(n, B, /*1, 1ll << n, 1, 1ll << n,*/ bs);
// std::cerr << "debug bs: \n";
// for(auto [n, from, to, hibiki]: bs) std::cerr << n << " (" << from.x << ", " << from.y << "), (" << hibiki.x << ", " << hibiki.y << ")\n";
size_t i = 0, j = 0;
llsi ans = 0;
while(i < as.size() || j < bs.size()) {
if(i < as.size() && j < bs.size() &&
as[i].n == bs[j].n &&
as[i].hibiki == bs[j].hibiki
) {
ans += as[i].from.dis(bs[j].from);
A = B;
break;
}
if(j == bs.size() || i < as.size() && as[i].n < bs[j].n) ans += as[i].from.dis(as[i].to), i += 1;
else ans += bs[j].from.dis(bs[j].to), j += 1;
}
llsi U = 1ll << n;
if(!(A == B)) {
if(A.y > 0 && A.y < U && B.y > 0 && B.y < U && (A.x <= 0 && B.x >= U || A.x >= U && B.x <= 0))
ans += std::min(
U - A.y + U - B.y + std::abs(A.x - B.x),
A.y - 0 + B.y - 0 + std::abs(A.x - B.x)
);
else
if(A.x > 0 && A.x < U && B.x > 0 && B.x < U && (A.y <= 0 && B.y >= U || A.y >= U && B.y <= 0))
ans += std::min(
U - A.x + U - B.x + std::abs(A.y - B.y),
A.x - 0 + B.x - 0 + std::abs(A.y - B.y)
);
else
ans += A.dis(B);
}
std::cout << ans << char(10);
}
return 0;
}
F. It's About Time
大力枚举+小推式子,对于徐神来说就是洒洒水
#include <bits/stdc++.h>
int main() {
const long double pi = std::acos(-1.L);
long double r, s, h;
std::cin >> r >> s >> h;
const long double tyear = 2.L * pi * r / (s * h);
const long double cy = std::round(tyear);
const long double ly = cy < tyear ? cy + 1.L : cy - 1.L;
long double mindelta = 1e9;
std::array<int, 3> ans;
for(int n1 = 2; n1 < 1000; ++n1) {
for(int n2 = n1 + n1; n2 < 1000; n2 += n1) {
for(int n3 = n2 + n2; n3 <= 1000; n3 += n2) {
const int l = n3 / n1 - n3 / n2 + n3 / n3;
const int c = n3 - l;
const long double aver = (l * ly + c * cy) / (long double)n3;
if(std::abs(aver - tyear) < mindelta) {
mindelta = std::abs(aver - tyear);
ans = {n1, n2, n3};
}
}
}
}
std::cout << ans[0] << ' ' << ans[1] << ' ' << ans[2] << std::endl;
return 0;
}
G. Pea Pattern
这种乍一看不知道有什么好做法,但是一看榜被过穿了的题,首先考虑大力出奇迹
直觉告诉我们这个序列做到后面很快就会产生重复,因此如果有解的话直接暴力模拟即可
#include<cstdio>
#include<iostream>
#include<set>
#include<string>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
string n,m;
int main()
{
RI i; cin>>n>>m; set <string> ext;
if (n.size()>100||m.size()>100) return puts("I'm bored."),0;
for (int cnt=1;;++cnt)
{
if (n==m) return printf("%d",cnt),0;
if (ext.count(n)) break; ext.insert(n);
static int c[10]; memset(c,0,sizeof(c));
for (i=0;i<n.size();++i) ++c[n[i]-'0'];
string t=""; for (i=0;i<10;++i) if (c[i]) t+=to_string(c[i])+char('0'+i); n=t;
}
return puts("Does not appear"),0;
}
H. Picking Up Steam
好难的几何,大致有个思路但貌似细节很多不太好写
I. Road To Savings
祁神写的,我题目都没看
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int INF = (int)1e8+5;
const int N = 205;
int n, m, src, dr;
int dis[N][N];
struct Edge{int a, b, w;};
vector<Edge> G;
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m >> src >> dr;
for (int i=1; i<=n; ++i){
for (int j=i+1; j<=n; ++j){
dis[i][j] = dis[j][i] = INF;
}
}
for (int i=1; i<=m; ++i){
int a, b, w; cin >> a >> b >> w;
dis[a][b] = dis[b][a] = w;
G.push_back(Edge{a, b, w});
}
for (int k=1; k<=n; ++k){
for (int i=1; i<=n; ++i){
for (int j=1; j<=n; ++j){
if (dis[i][k]+dis[k][j]<dis[i][j]) dis[j][i]=dis[i][j] = dis[i][k]+dis[k][j];
}
}
}
int dist = dis[src][dr];
int ans=0;
//
// for (int i=1; i<=n; ++i){
// for (int j=1; j<=n; ++j){
// printf("%d ", dis[i][j]);
// }puts("");
// }
for (auto [a, b, w] : G){
// printf("(%d %d) %d %d\n", a, b, dis[src][a]+dis[b][dr], dis[src][b]+dis[a][dr]);
if (dis[src][a]+dis[b][dr]+w > dist && dis[src][b]+dis[a][dr]+w > dist) ans += w;
}
cout << ans;
return 0;
}
J. Simple Solitaire
徐神写的,我题目都没看(怎么又是打牌啊)
#include <bits/stdc++.h>
bool shrink(std::vector<std::string> &s) {
// std::cerr << "Before: ";
// for(int i = 0; i < s.size(); ++i) std::cerr << s[i] << char(i == s.size() - 1 ? 10 : 32);
// std::cerr << std::flush;
int pos = -1, type = 0;
for(int i = s.size() - 1; i >= 3; --i) {
if(s[i][0] == s[i - 3][0] && type < 4)
pos = i, type = 4;
if(s[i][1] == s[i - 3][1] && type < 2)
pos = i, type = 2;
}
if(type == 4)
s.erase(s.begin() + (pos - 3), s.begin() + (pos + 1));
if(type == 2)
s.erase(s.begin() + (pos - 3)),
s.erase(s.begin() + (pos - 1));
// std::cerr << "After: ";
// for(int i = 0; i < s.size(); ++i) std::cerr << s[i] << char(i == s.size() - 1 ? 10 : 32);
// std::cerr << std::flush;
return type > 0;
}
int main() {
std::vector<std::string> deck(52), st;
for(auto &card: deck) std::cin >> card;
for(auto card: deck) {
st.push_back(card);
while(shrink(st));
}
std::cout << st.size();
for(int i = 0; i < st.size(); ++i) std::cout << char(32) << st[i];
std::cout << std::endl;
return 0;
}
K. Two Charts Become One
因为没判两个树都是单点的Corner Case红温了好久,浪费了好多时间
根据题目中对于树的同构的定义,其实只要判每个点的儿子按顺序重排后是否相同即可
更方便的写法是直接把每条边按照父亲到儿子的顺序加入集合中,最后比较两个数对应的边集合是否相同即可
然后注意下模拟建树的过程即可,由于只有一个点的时候不存在边因此可以假设一个编号为\(0\)的虚点套在最外层
#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<stack>
#include<set>
#include<cctype>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
string A,B;
inline vector <pi> solve(string s)
{
RI i,j; string t=""; vector <pi> edges;
for (i=0;i<s.size();++i) if (!isspace(s[i])) t+=s[i];
t="(0("+t+"))"; stack <int> stk; set <pi> rst;
for (i=0;i<t.size();++i)
{
if (t[i]=='(') { stk.push(i); continue; }
if (t[i]==')')
{
int pos=stk.top(); stk.pop();
int x=0; for (j=pos+1;;++j)
if (isdigit(t[j])) x=x*10+t[j]-'0'; else break;
for (auto it=rst.upper_bound(pi(pos,0));it!=rst.end();it=rst.erase(it))
edges.push_back(pi(x,it->se));
rst.insert(pi(pos,x)); continue;
}
}
sort(edges.begin(),edges.end());
return edges;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
getline(cin,A); getline(cin,B);
return puts(solve(A)==solve(B)?"Yes":"No"),0;
}
L. Which Warehouse?
很裸的一个题
由于每个点不能选两种及以上的物资,因此我们可以把问题转化为一个二分图最小权匹配问题
左边\(n\)个点代表原图中的点,右边\(m\)个点代表物资,两点间连边对应将所有该类物资移动到对应点上的代价和,这个可以预处理最短路后快速计算
然后直接上KM即可,注意KM要求二分图两边点的数量相同,这点也很好解决,直接在右边多开\(n-m\)个点,表示对应点不作为存储地点,与左边所有点的边权都是\(0\)即可
#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=105,INF=1e18;
int n,m,c[N][N],dis[N][N];
namespace KM
{
int w[N][N],kx[N],ky[N],py[N],vy[N],slk[N],pre[N];
inline int solve(CI n)
{
RI i,j,k; for (i=1;i<=n;++i) for (j=1;j<=n;++j) kx[i]=max(kx[i],w[i][j]);
for (i=1;i<=n;++i)
{
for (j=0;j<=n;++j) vy[j]=pre[j]=0,slk[j]=INF;
int k=0,p=-1; for (py[k=0]=i;py[k];k=p)
{
int d=INF; vy[k]=1; int x=py[k];
for (j=1;j<=n;++j) if (!vy[j])
{
int t=kx[x]+ky[j]-w[x][j];
if (t<slk[j]) slk[j]=t,pre[j]=k;
if (slk[j]<d) d=slk[j],p=j;
}
for (j=0;j<=n;++j)
if (vy[j]) kx[py[j]]-=d,ky[j]+=d; else slk[j]-=d;
}
for (;k;k=pre[k]) py[k]=py[pre[k]];
}
int ret=0; for (i=1;i<=n;++i) ret+=kx[i]+ky[i];
return -ret;
}
};
using namespace KM;
signed main()
{
RI i,j,k; for (scanf("%lld%lld",&n,&m),i=1;i<=n;++i)
for (j=1;j<=m;++j) scanf("%lld",&c[i][j]);
for (i=1;i<=n;++i) for (j=1;j<=n;++j)
if (scanf("%lld",&dis[i][j]),dis[i][j]==-1) dis[i][j]=INF;
for (k=1;k<=n;++k) for (i=1;i<=n;++i) for (j=1;j<=n;++j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for (i=1;i<=n;++i) for (j=1;j<=m;++j)
{
int cur=0; for (k=1;k<=n;++k) cur+=dis[k][i]*c[k][j];
w[i][j]=-cur;
}
return printf("%lld",solve(n)),0;
}
Postscript
每天被花式腐乳,真是菜菜又鸡鸡啊