一种新的方法,做法类似于 w33z 的毒瘤题团,大家可以类比着看一下。
团这道题启示我们什么?对于 (n) 个点向 (m) 个点分别连边,我们可以引入中介来讲 (nm) 条边变成 (n+m) 条边。
相似的道理,首先考虑如果 (S_{i,i}) 保证为 (1) 怎么做?我们可以将同一个种类的所有相邻两头奶牛的连边。每一个奶牛对于一种颜色,就只需要跟左边最近的和右边最近的连边就可以了(当然如果左边或右边没有这个种类的奶牛就自然不用了)。
我们回过来再考虑(S_{i,i}) 可能为 (0),也就是说不能再同种类的相邻的两头奶牛建边了。我们给每个点建一个虚点,虚点向这个点连边,我们就可以在同一个种类的所有相邻两头奶牛的虚点连边,每一个奶牛对于一种颜色,就只需要跟左边最近的和右边最近的虚点连边就可以了(当然如果左边或右边没有这个种类的奶牛就自然不用了)。
相比于分层图的解法,代码有些难写:
#include<bits/stdc++.h>
#define log(a) cerr<<" 33[32m[DEBUG] "<<#a<<'='<<(a)<<" @ line "<<__LINE__<<" 33[0m"<<endl
#define LL long long
#define eb emplace_back
#define SZ(x) ((int)x.size()-1)
#define ms(a,b) memset(a,b,sizeof a)
#define F(i,a,b) for(int i=(a);i<=(b);++i)
#define DF(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
inline int read(){char ch=getchar(); int w=1,c=0;
for(;!isdigit(ch);ch=getchar()) if (ch=='-') w=-1;
for(;isdigit(ch);ch=getchar()) c=(c<<1)+(c<<3)+(ch^48);
return w*c;
}
const int N=1e5+10;
int b[N],cnt,lst[55][N],f[N];
bool h[55][55];
vector<pair<int,int> >v[N];
queue<int>q;
int main(){
int n=read(),k=read();
F(i,1,n){
b[i]=read();
v[i+n].eb(i,0);
if(lst[b[i]][0]){
v[i+n].eb(lst[b[i]][lst[b[i]][0]]+n,i-lst[b[i]][lst[b[i]][0]]);
v[lst[b[i]][lst[b[i]][0]]+n].eb(i+n,i-lst[b[i]][lst[b[i]][0]]);
}lst[b[i]][++lst[b[i]][0]]=i;
}
F(i,1,k)
F(j,1,k){
char ch=getchar();
for(;ch!='0'&&ch!='1';ch=getchar());
h[i][j]=(ch=='1');
}
F(i,1,n)
F(j,1,k)
if(h[b[i]][j]){
if(!lst[j][0])continue;
if(lst[j][1]<=i){
int x=upper_bound(lst[j]+1,lst[j]+lst[j][0]+1,i)-lst[j]-1;
v[i].eb(lst[j][x]+n,i-lst[j][x]);
}
if(lst[j][lst[j][0]]>i){
int x=upper_bound(lst[j]+1,lst[j]+lst[j][0]+1,i)-lst[j];
v[i].eb(lst[j][x]+n,lst[j][x]-i);
}
}
ms(f,0x3f);f[1]=0;
q.push(1);
while(q.size()){
int x=q.front();q.pop();
for(auto[fi,se]:v[x]){
if(f[x]+se<f[fi]){
f[fi]=f[x]+se;
q.push(fi);
}
}
}
if(f[n]==f[0])puts("-1");
else cout<<f[n];
return 0;
}