jzoj 3857. 【NOIP2014八校联考第3场第1试10.4】反抗希碧拉系统续(regex)
这题是要玩死我的节奏啊。。。
考场打完\(T1\)、\(T3\)然后只剩\(30min\),而且这题也没十分弄清楚题意,所以\(GG\)。
但就算考试后知道了正解,还是有些糊涂。
到了晚上听完讲(其实只听了设个二维数组就走了,因为我感觉自己已经懂了),才渐渐弄清楚了思路,呵呵,码完即可(* ̄︶ ̄)。
由于元素的种类最多只有5个,所以我们可以联想到状压。
然后我们可以通过题目中的那些转移方式(如(表达式)+中可以从表达式最右边的元素走到最左边的元素,[元素][元素]中我们就可以从第一个元素走到第二个元素),这样我们就可以构成一个图。
设\(f[i][s]\)表示走了\(i\)步,当前可能到达的元素的状压状态为\(s\)的方案数,这种设法使为了防止计算重复的。
直接转移显然不行,所以我们需要预处理出其它的东西。
我们设\(to[i][j]\)表示从\(i\)这个状态转移到\(j\)这个状态时都能走同一种串的方案数。
如此状态转移方程也就自然出来了:\(f[i][s]=f[i-1][s']*to[s'][s]\)
其实这个设法是十分巧妙的。因为我们不这样设的话,就可能会有某些神奇的东东算重然后\(GG\)。(我之前打得时候设的不一样导致答案一直很大)
对于这个转移,我们枚举\(i\),以及要转移的那个相同的字符是什么,然后对于\(i\)中的每个元素,我们找到它所连向的包含这个字符的元素,并求出其状压状态\(s\),然后我们就可以将\(to[i][s]\)加\(1\)。
解释:因为我们对于\(i\)到\(s\)的路径全都是相同的,因为它们在这种情况下加的都是同一种字符,所以我们只需要对此方案数加\(1\)即可。而其它字符会在其它时候做掉。
我们在预处理出这个东西以后,可以发现转移可以用矩乘优化。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 510
#define mo 4294967296ll
#define ll long long
#define mem(x, a) memset(x, a, sizeof x)
#define mpy(x, y) memcpy(x, y, sizeof y)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
#define go(x) for (int p = tail[x], v; p; p = e[p].fr)
using namespace std;
int n, len, ys[7][27], tot = 0;
int ke[80], cf[7], gs[7], z[7], top = 0, gt[27];
ll f[80][80], c[80][80], to[80][80], ans = 0;
char s[N];
template<typename T>
inline void read(T& x)
{
x = 0; int f = 0; char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
if (f == 1) x = -x;
}
ll gsc(ll x, ll y)
{
ll s = 0;
while (y)
{
if (y & 1) s = (s + x) % mo;
x = (x + x) % mo, y >>= 1;
}
return s;
}
int main()
{
freopen("regex.in", "r", stdin);
freopen("regex.out", "w", stdout);
scanf("%s%d", s + 1, &n);
len = strlen(s + 1);
cf[0] = 1; fo(i, 1, 6) cf[i] = cf[i - 1] << 1;
fo(i, 1, len)
{
if (s[i] == '[')
{
i++, tot++, ke[tot - 1] |= cf[tot - 1];
while (s[i] != ']') gs[tot]++, ys[tot][s[i] - 'a'] = 1, i++;
}
else if (s[i] == '(') z[++top] = tot + 1;
else if (s[i] == ')') ke[tot] |= cf[z[top] - 1], top--;
}
fo(i, 1, cf[tot] - 1)
{
mem(gt, 0);
fo(j, 1, tot)
{
if (! (i & cf[j - 1])) continue;
fo(k, 1, tot)
{
if (! (ke[j] & cf[k - 1])) continue;
fo(l, 0, 25) if (ys[k][l]) gt[l] |= cf[k - 1];
}
}
fo(j, 0, 25) to[i][gt[j]]++;
}
f[1][1] = gs[1], n--;
while (n)
{
if (n & 1)
{
mem(c, 0);
fo(k, 1, cf[tot] - 1)
fo(j, 1, cf[tot] - 1)
(c[1][j] += gsc(f[1][k], to[k][j]) % mo) %= mo;
mpy(f, c);
}
mem(c, 0);
fo(k, 1, cf[tot] - 1)
fo(i, 1, cf[tot] - 1)
fo(j, 1, cf[tot] - 1)
(c[i][j] += gsc(to[i][k], to[k][j]) % mo) %= mo;
mpy(to, c);
n >>= 1;
}
fo(i, 1, cf[tot] - 1)
if (i & cf[tot - 1]) ans += f[1][i];
printf("%lld\n", ans % mo);
return 0;
}