题解 CF1383C 【String Transformation 2】
可以将原问题转化为如下问题
给你一张初始无边的图
G ,存在20 个点,分别表示a\rightarrow t 的20 个字母.现在要求你按时间顺序连边,使得对于每个位置i ,从A_i 到B_i 都存在一条连边时间单调递增的路径,求最小边数.
再构建一张图
显然,对于
设这个弱联通块为
显然,我们可以使用
- 连边
1\rightarrow 2,2\rightarrow 3,...,n-1\rightarrow n . - 连边
n\rightarrow 1 . - 连边
1\rightarrow 2,2\rightarrow 3,...,n-2\rightarrow n-1 .
不过,这样连边不一定是最优的.
具体的,我们定义 DAG导出子图 为导出子图
设
将最大DAG导出子图中的点按拓扑序编号为
- 连边
1\rightarrow 2,2\rightarrow 3,...,m-1\rightarrow m,m\rightarrow m+1 ,边数为m . - 连边
m+1\rightarrow 1 ,边数为1 . - 按上文提到的连边方法将
m+1\rightarrow n 连边,边数为(n-m)\times 2-2 .
感性理解一下,
首先,对于一个
然后,根据上面的定义,可以发现整个图相当于一个强连通分量上每个点连出去若干个DAG,所有这些DAG构成最大DAG导出子图.所以
至于实现,对于判断一个点集是否是DAG导出子图可以
int n;
char A[100005],B[100005];
int fa[201];
inline int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
int mp[22];
int f[1<<20];
bool dfs(int now)
{
if(now==0)return 1;
if(f[now]!=-1)return f[now];
f[now]=0;
for(int i=1;i<=n;i++)
if(now>>(i-1)&1)
if(((mp[i]>>1)&now)==0)
f[now]|=dfs(now^(1<<(i-1)));
return f[now];
}
int calc(int x)
{
int re=0;
for(int i=0;i<n;i++)
if(x>>i&1)
re++;
return re;
}
void solve()
{
read(n);
scanf("%s",A+1);
scanf("%s",B+1);
for(int i=1;i<=20;i++)fa[i]=i;
memset(mp,0,sizeof(mp));
for(int i=1;i<=n;i++)
{
if(A[i]==B[i])continue;
int fat=find(A[i]-'a'+1),fbt=find(B[i]-'a'+1);
mp[A[i]-'a'+1]|=1<<(B[i]-'a'+1);
if(fat==fbt)continue;
fa[fat]=fbt;
}
int ans=0;
n=20;
for(int i=1;i<=n;i++)
if(find(i)==i)
ans--;
memset(f,-1,sizeof(f));
f[0]=1;
int mx=0;
for(int i=0;i<(1<<n);i++)
if(dfs(i))
mx=max(mx,calc(i));
ans=ans-mx+n*2;
printf("%d\n",ans);
}
int main()
{
int T;read(T);
while(T--)solve();
return 0;
}