zoukankan      html  css  js  c++  java
  • 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
    ", ans % mo);
    	return 0;
    }
    
    
    转载需注明出处。
  • 相关阅读:
    Eclipse项目上传和下载到码云上
    java.lang.IllegalArgumentException,java.util.zip.ZipException 解决办法
    #{}和${}的区别
    Lambda 表达式
    存储器的按字寻址和按字节寻址
    二叉树的三种遍历方式
    线性表和链表
    java泛型理解
    java字符输入输出流
    applet的生命周期
  • 原文地址:https://www.cnblogs.com/jz929/p/12210329.html
Copyright © 2011-2022 走看看