题解:12.14 模拟赛解题报告 神秘大矢

后话:这已经不是写过最长的代码了。

纪念历史性时刻

Description

你是游戏设计师,你要设计所有长度为K的字符串,每个字符是 ABCD,显然共有\(4^K\)个不同的字符串,你要给每个字符串都分配一种颜色,不同的字符串可以分配相同的颜色,颜色分配过程是由你来决定的。

奶牛Bessie是这个游戏的玩家,首先Bessie会输入一个长度是K的字符串S(每个字符也是ABCD),然后Bessie每一步操作是如下两种选择之一:

1、 交换当前字符串\(S\)的相邻的两个字符。注意,第一个字符和最后一个字符不算相邻,即\(S\)不能看成一个环。

2、如果当前字符串\(S\)含有子串\(b[i]\),那么可以把该子串替换成字符串\(c[i]\)。其中\(b\)数组和\(c\)数组都是字符串数组,作为输入数据给出来的。

如果Bessie能够通过上面的操作,使得从字符串S出发,通过若干次操作之后,能够变成字符串\(T\)\(T\)\(S\)是不同的字符串),且字符串\(T\)和字符串\(S\)是相同颜色的,那么Bessie就会胜利了。

作为游戏设计师的你,你的目的是想让Bessie永远都不可能胜利,也就是说无论Bessie输入的字符串\(S\)是什么,都不可能胜利。

显然,这与你对\(4^K\)个不同的字符串如何分配颜色是非常重要的。现在的问题是,你至少需要多少种不同的颜色,才能使得Bessie永远不可能胜利。

Analysis

先考虑将多个状态压缩成\([a_1\space a_2\space a_3\space a_4]\),表示\(a_1\)A\(a_2\)B\(a_3\)C\(a_4\)D组成的字符串,显然,这样会使状态数大大减少,空间复杂度降为 \(O(n^3)\)

其次考虑,如果状态\(A\)可以通过若干次操作得到状态\(B\),则称它们是联通的。通过对可以一步达到的状态连接边,对它们进行缩点。

观察到,一个联通块的贡献取决于块中最长路径长度。路径的长度定义为路径上的点权之和,而点权指一个状态中的可能字符串数量。

如何计算可能字符串数量呢?观察到\(30!\)达到了惊人的\(10^{32}\)以上,所以方案一即\(\frac{K!}{a_1!a_2!a_3!a_4!}\)作废。然而还可采用另一种方法,即 \(\binom{K}{a_1}\binom{K-a_1}{a_2}\binom{K-a_1-a_2}{a_3}\),预处理一下杨辉三角即可轻松算出。

最后,拓扑dp处理最长路径长度。代码总长度4k,非常令人难评,暂时是写过最长的代码。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=55,M=55*55*55;
LL G,K,n;
string B,C;
int cnt;
int id[N][N][N][N];
LL siz[M];
LL qkpow(LL x,LL y){
	LL res=1;
	while(y){
		if(y&1)res=res*x;
		x=x*x;
		y>>=1;
	}
	return res;
}
struct Node{
	int a[4];
}idv[M];
LL f[55][55];
int cf[N];
LL cal(Node x){
	LL tot=f[K][x.a[0]];
	tot*=f[K-x.a[0]][x.a[1]];
	tot*=f[K-x.a[0]-x.a[1]][x.a[2]];
	return tot;
}
struct Lim{
	int b[4];
	int c[4];
}A[N];
int gt(Node x){
	return id[x.a[0]][x.a[1]][x.a[2]][x.a[3]];
}
bool operator == (Lim x,Lim y){
	for(int i=0;i<4;i++)
		if(x.b[i]!=y.b[i])
			return false;
	for(int i=0;i<4;i++)
		if(x.c[i]!=y.c[i])
			return false;
	return true;
}
bool operator >= (Node x,Node y){
	for(int i=0;i<4;i++)
		if(x.a[i]<y.a[i])
			return false;
	return true;
}
Node operator - (Node x,Node y){
	Node ans=x;
	for(int i=0;i<4;i++)
		ans.a[i]-=y.a[i];
	return ans;
}
Node operator + (Node x,Node y){
	Node ans=x;
	for(int i=0;i<4;i++)
		ans.a[i]+=y.a[i];
	return ans;
}
bool cmp(Lim x,Lim y){
	for(int i=0;i<4;i++){
		if(x.b[i]!=y.b[i])
			return x.b[i]<y.b[i];
	}
	for(int i=0;i<4;i++){
		if(x.c[i]!=y.c[i])
			return x.c[i]<y.c[i];
	}
}
Node inp(int a,int b,int c,int d){
	Node ans;
	ans.a[0]=a;ans.a[1]=b;
	ans.a[2]=c;ans.a[3]=d;
	return ans;
}
LL ans;
int idx,head[M],cne;
struct Edge{
	int v,next;
}adj[M*2],ade[M*2];
int stk[M],tp,sccno[M];
int scnt,dfn[M],low[M],rd[M],idk;
LL rsiz[M];
void ins(int x,int y){
	adj[++idx].v=y;
	adj[idx].next=head[x];
	head[x]=idx;
}
LL dp[M];
void init(){
	cne=0;idk=0;
	cnt=0;ans=0;tp=0;scnt=0;
	idx=0;
	for(int i=1;i<=n;i++){
		for(int k=0;k<4;k++){
			A[i].b[k]=A[i].c[k]=0;
		}
	}
	for(int i=0;i<=K;i++)
		for(int j=0;i+j<=K;j++)
			for(int k=0;i+j+k<=K;k++){
				int l=K-i-j-k;
				id[i][j][k][l]=++cnt;
				sccno[cnt]=dfn[cnt]=0;
				low[cnt]=0;
				head[cnt]=0;
				dp[cnt]=0;
				rd[cnt]=0;rsiz[cnt]=0;
				idv[cnt]=inp(i,j,k,l);
				siz[cnt]=cal(idv[cnt]);
			}
}
void tarjan(int x){
	dfn[x]=low[x]=++idk;
	stk[++tp]=x;
	for(int i=head[x];i;i=adj[i].next){
		int v=adj[i].v;
		if(!dfn[v]){
			tarjan(v);
			low[x]=min(low[v],low[x]);
		}
		else if(!sccno[v])low[x]=min(dfn[v],low[x]);
	}
	if(low[x]==dfn[x]){
		scnt++;
		while(tp>0){
			int v=stk[tp--];
			sccno[v]=scnt;
			rsiz[scnt]+=siz[v];
			if(v==x)break;
		}
	}
}
LL dfs(int x){
	if(dp[x])return dp[x];
	dp[x]=rsiz[x];
	for(int i=head[x];i;i=adj[i].next){
		int v=adj[i].v;
		dp[x]=max(dp[x],rsiz[x]+dfs(v));
	}
	return dp[x];
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	for(int i=0;i<=50;i++)
		f[i][0]=f[i][i]=1;
	for(int i=2;i<=50;i++)
		for(int j=1;j<i;j++)
			f[i][j]=f[i-1][j]+f[i-1][j-1];
	cin>>G;
	while(G--){
		cin>>K>>n;
		init();
		for(int i=1;i<=n;i++){
			string B;
			cin>>B;
			for(int j=0;j<B.size();j++)
				for(int k=0;k<4;k++)
					A[i].b[k]+=(B[j]==(char)(k+'A'));
		}
		for(int i=1;i<=n;i++){
			string C;
			cin>>C;
			for(int j=0;j<C.size();j++)
				for(int k=0;k<4;k++)
					A[i].c[k]+=(C[j]==(char)(k+'A'));
		}
		sort(A+1,A+1+n,cmp);
		n=unique(A+1,A+1+n)-(A+1);
		for(int i=1;i<=cnt;i++){
			for(int j=1;j<=n;j++){
				Node arg=idv[i];
				Node leo=inp(A[j].b[0],A[j].b[1],A[j].b[2],A[j].b[3]);
				Node oel=inp(A[j].c[0],A[j].c[1],A[j].c[2],A[j].c[3]);
				if(arg>=leo){
					Node tr=arg-leo+oel;
					ins(i,gt(tr));
					ade[++cne]=(Edge){i,gt(tr)};
				}
			}
		}
		for(int i=1;i<=cnt;i++){
			if(!dfn[i])tarjan(i);
		}
		idx=0;
		for(int i=1;i<=scnt;i++)
			head[i]=0;
		for(int i=1;i<=cne;i++){
			if(sccno[ade[i].v]!=sccno[ade[i].next]){
				ins(sccno[ade[i].v],sccno[ade[i].next]);
				rd[sccno[ade[i].next]]++;
			}
		}
		for(int i=1;i<=scnt;i++){
			if(!rd[i]){
				ans=max(ans,dfs(i));
			}
		}
		cout<<ans<<'\n';
	}
	return 0;
}
posted @ 2025-08-11 10:50  TBSF_0207  阅读(10)  评论(0)    收藏  举报