描述
小A在8102年掌握了穿越时间的科技,他想要回归遥远的2018年来看一看。
我们定义“时间线”是长度恰好为 n ,仅由 ABCD 四个大写字母组成的字符串。
设某个时间线为 S,小A可以用以下两种方式修改时间线:
交换 S 中相邻两个字符。
使用某种转移。一个转移是两个长度不超过 n的等长字符串 (Ai,Bi),可以把 S 中某个子串 Ai 替换成 Bi。
小A自然能随意行走于时间线上,但他想享受这跃迁的过程。他想考考你:初始的时间线任选,任意操作,在任意一条时间线结束,途中经过的不同时间线数量最多是多少?
注意可以经过重复的时间线,但只会计入一次答案。
输入格式
第一行两个数 n , m,表示时间线的长度和转移的个数。
后面 m 行,每行两个等长字符串 Ai,Bi,表示一个转移。
输出格式
一行,一个整数表示最大答案。
样例1
样例输入1
2 3
A B
A C
D D
样例输出1
5
样例解释1
一种最优方案:AA→AB→BA→BC→CB
限制与约定
对于 30% 的数据, n≤5, m≤5。
对于 50% 的数据, n≤10, m≤30。
对于 100% 的数据, n≤30, m≤50。
时间限制:2s
空间限制:512MB
一种很显然的暴力就是记录状态,然后根据给定的条件连边,tarjan缩点之后求最长路
常数写得好有60分
实际上有了相邻转移之后我们发现,如果A,B,C,D的个数都确定,他能通过相邻交换转移到任意状态
那我们就可以根据A,B,C,D的个数设计状态,大力算出每个点的权值再重复上述操作即可
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
int n , m;
int id[31][31][31][31] , tot , tt;
int linkk[6100] , t , du[6100];
int dfn[6100] , low[6100] , cnt , cor[6100] , totc;
ll a[6100] , v[6100];
ll dp[6100];
ll jc[30];
struct node{
int n,y,x;
}e[181000];
int read(){int sum = 0;char c = getchar();bool flag = true;while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();if(flag) return sum;else return -sum;}
ll calc(int A,int B,int C)
{
int D = n - A - B - C;
ll sum = 1;
int a , b , c , d;a = b = c = d = 1;
rep(i,1,n)
{
sum *= i;
for(;a <= A && sum % a == 0;sum/=a,a++);
for(;b <= B && sum % b == 0;sum/=b,b++);
for(;c <= C && sum % c == 0;sum/=c,c++);
for(;d <= D && sum % d == 0;sum/=d,d++);
}
return sum;
}
void pre(){rep(a,0,n) rep(b,0,n-a) rep(c,0,n-a-b) id[a][b][c][n-a-b-c] = ++tot,v[tot] = calc(a,b,c);return;}
char in[40];
void insert(int x,int y)
{
e[++t].y = y;e[t].x = x;e[t].n = linkk[x];linkk[x] = t;
return;
}
void init()
{
rep(x,1,m)
{
int n1,n2,n3,n4;
int m1,m2,m3,m4;
n1 = n2 = n3 = n4 = m1 = m2 = m3 = m4 = 0;
scanf("%s",in+1);
int len = strlen(in+1);
rep(i,1,len)
if(in[i] == 'A') n1++;
else if(in[i] == 'B') n2++;
else if(in[i] == 'C') n3++;
else n4++;
scanf("%s",in+1);
len = strlen(in+1);
rep(i,1,len)
if(in[i] == 'A') m1++;
else if(in[i] == 'B') m2++;
else if(in[i] == 'C') m3++;
else m4++;
m1=n1-m1;m2=n2-m2;m3=n3-m3;m4=n4-m4;
rep(a,n1,n) rep(b,n2,n-a) rep(c,n3,n-a-b)
{
int d = n - a - b - c;
if(d < n4) continue;
insert(id[a][b][c][d],id[a-m1][b-m2][c-m3][d-m4]);
tt++;
}
}
return;
}
stack<int>st;
bool f[6100];
void tarjan(int x)
{
dfn[x] = low[x] = ++cnt;
f[x] = true;st.push(x);
for(int i = linkk[x];i;i = e[i].n)
{
int y = e[i].y;
if(!dfn[y])
{
tarjan(y);
low[x] = min(low[x] , low[y]);
}
else
if(f[y]) low[x] = min(low[x] , dfn[y]);
}
if(low[x] == dfn[x])
{
ll k = v[x];
cor[x] = ++totc;f[x] = false;
while(st.top() != x)
cor[st.top()] = totc,
f[st.top()] = false,k += v[st.top()],
st.pop();
st.pop();a[totc] = k;
}
return;
}
void sd()
{
memset(linkk,0,sizeof(linkk));t = 0;
rep(i,1,tt)
{
int x = e[i].x , y = e[i].y;
if(cor[x] != cor[y])
insert(cor[x] , cor[y]) , du[cor[y]]++;
}
return;
}
queue<int>q;
ll ans = 0;
void topsort()
{
rep(i,1,tot) if(!du[i]) q.push(i);
while(!q.empty())
{
int x = q.front();
dp[x] += a[x];
ans = max(ans,dp[x]);
for(int i = linkk[x];i;i = e[i].n)
{
int y = e[i].y;
du[y]--;
dp[y] = max(dp[y] , dp[x]);
if(!du[y]) q.push(y);
}
q.pop();
}
return;
}
int main()
{
n = read();m = read();
pre();
init();rep(i,1,tot)if(!dfn[i])tarjan(i);sd();
topsort();
printf("%lld\n",ans);
return 0;
}