【YBT2022寒假Day7 A】字符变换(状压)(Tarjan)(DP)

字符变换

题目链接:YBT2022寒假Day7 A

题目大意

给你一个只包含四种字符的字符串,和一些二元组 (x,y),x,y 为两个等长的字符串。
一个二元组的作用是可以选择字符串中一个与 x 相同的子串把它变成 y。
你也可以任意交换字符串两个位置的字符。
然后问你从任意字符串开始进行任意次变换,最多能得到多少个字符串。

思路

你考虑因为可以交换位置,所以字符串我们只需要关心它每个字符的个数,至于这个的方案你就可以用 n ! a ! b ! c ! ( n − a − b − c ) ! \dfrac{n!}{a!b!c!(n-a-b-c)!} a!b!c!(nabc)!n! 来求。
然后要注意的是因为 n n n 有三十,不能直接预处理阶乘,我们就分解质因数之类的搞即可。

然后你发现如果通过变换某个状态 x x x 可以变到某个状态 y y y,那我们可以把它们连一条有向边,代表如果你变换到 x x x,你可以继续变换到 y y y
然后你可以用 Tarjan 缩点,然后我们考虑这个状态的数量。

然后发现字符串长度很小,我们考虑状压,发现状态数不多可以搞,设 f x f_{x} fx 为从任意点开始走到 x x x 的最大数量。
(压三个数的即可,第四个直接用 n n n 减去前三个就是了)

然后这么搞 Tarjan 完 DP 一下就得到答案了。

代码

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#define ll long long
#define MAXN ((n + 1) * (n + 1) * (n + 1))

using namespace std;

struct awa {
	int a[4];
};
struct node {
	int to, nxt;
}e[6000001], e_[6000001];
int n, m, le[1000001], KK, a[1001][5], b[1001][5], sz[1001];
int le_[1000001], KK_;
char s[31], ss[31];
ll val[1000001];

vector <int> q;

int get_(int a, int b, int c, int d) {
	return a * (n + 1) * (n + 1) + b * (n + 1) + c;
}

ll clac(awa now) {
	q.clear();
	for (int i = 0; i < 4; i++) {
		for (int j = 2; j <= now.a[i]; j++) q.push_back(j);
	}
//	__int128 re = 1;
	ll re = 1;
	for (int i = 2; i <= n; i++) {
		re *= i;
		for (int j = 0; j < q.size(); j++)
			if (q[j] != 1 && re % q[j] == 0) {
				re /= q[j]; q[j] = 1;
			}
	}
	return (ll)re;
}

int dfn[1000001], low[1000001], col[1000001], tmpt, sta[1000001], tot;
ll vals[1000001];

void add(int x, int y) {
	e[++KK] = (node){y, le[x]}; le[x] = KK;
}

void add_(int x, int y) {
	e_[++KK_] = (node){y, le_[x]}; le_[x] = KK_;
}

void tarjan(int now) {
	dfn[now] = low[now] = ++tmpt;
	sta[++sta[0]] = now;
	for (int i = le[now]; i; i = e[i].nxt)
		if (!dfn[e[i].to]) {
			tarjan(e[i].to);
			low[now] = min(low[now], low[e[i].to]);
		}
		else if (!col[e[i].to]) low[now] = min(low[now], low[e[i].to]);
	if (dfn[now] == low[now]) {
		col[now] = ++tot;
		vals[tot] = val[now];
		while (sta[sta[0]] != now) {
			vals[tot] += val[sta[sta[0]]];
			col[sta[sta[0]]] = tot;
			sta[0]--;
		}
		sta[0]--;
	}
}

queue <int> qq;
ll f[1000001], ans;
int du[1000001];

int main() {
//	freopen("character.in", "r", stdin);
//	freopen("character.out", "w", stdout);
	
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%s %s", s + 1, ss + 1); sz[i] = strlen(s + 1);
		for (int j = 1; j <= sz[i]; j++) a[i][s[j] - 'A']++, b[i][ss[j] - 'A']++;
	}
	
	for (int i = 0; i < MAXN; i++) {
		awa now;
		now.a[0] = i / (n + 1) / (n + 1) % (n + 1);
		now.a[1] = i / (n + 1) % (n + 1);
		now.a[2] = i % (n + 1);
		if (now.a[0] + now.a[1] + now.a[2] > n) continue;
		now.a[3] = n - now.a[0] - now.a[1] - now.a[2];
		val[i] = clac(now);
		for (int j = 1; j <= m; j++) {
			bool yes = 1;
			for (int k = 0; k < 4; k++)
				if (now.a[k] < a[j][k]) {
					yes = 0; break;
				}
			if (yes) {
				int to = get_(now.a[0] - a[j][0] + b[j][0], now.a[1] - a[j][1] + b[j][1], now.a[2] - a[j][2] + b[j][2], now.a[3] - a[j][3] + b[j][3]);
				if (i != to) add(i, to);
			}
		}
	}
	
	for (int i = 0; i < MAXN; i++) {
		awa now;
		now.a[0] = i / (n + 1) / (n + 1) % (n + 1);
		now.a[1] = i / (n + 1) % (n + 1);
		now.a[2] = i % (n + 1);
		if (now.a[0] + now.a[1] + now.a[2] > n) continue;
		now.a[3] = n - now.a[0] - now.a[1] - now.a[2];
		if (!dfn[i]) tarjan(i);
	}
	for (int i = 0; i < MAXN; i++) {
		awa now;
		now.a[0] = i / (n + 1) / (n + 1) % (n + 1);
		now.a[1] = i / (n + 1) % (n + 1);
		now.a[2] = i % (n + 1);
		if (now.a[0] + now.a[1] + now.a[2] > n) continue;
		now.a[3] = n - now.a[0] - now.a[1] - now.a[2];
		for (int j = 1; j <= m; j++) {
			bool yes = 1;
			for (int k = 0; k < 4; k++)
				if (now.a[k] < a[j][k]) {
					yes = 0; break;
				}
			if (yes) {
				int to = get_(now.a[0] - a[j][0] + b[j][0], now.a[1] - a[j][1] + b[j][1], now.a[2] - a[j][2] + b[j][2], now.a[3] - a[j][3] + b[j][3]);
				if (col[i] != col[to]) add_(col[i], col[to]), du[col[to]]++;
			}
		}
	}
	
	for (int i = 1; i <= tot; i++)
		if (!du[i]) {
			qq.push(i);
			f[i] = vals[i]; ans = max(ans, f[i]);
		}
	while (!qq.empty()) {
		int now = qq.front(); qq.pop();
		for (int i = le_[now]; i; i = e_[i].nxt) {
			int x = e_[i].to;
			f[x] = max(f[x], f[now]);
			du[x]--;
			if (!du[x]) {
				qq.push(x);
				f[x] += vals[x]; ans = max(ans, f[x]);
			}
		}
	}
	printf("%lld", ans);
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值