zoukankan      html  css  js  c++  java
  • [题解向] Manacher简单习题

    (1) LG1659 [国家集训队]拉拉队排练

    求前(k)大的奇数长度回文串的长度之积。

    ( m |S|leq 1e6,Kleq 1e12)

    ……一开始觉得挺水,就开始二分最少长度能到多少。写写写…写到最后发现细节很烦人…然后最后发现是错的qaq

    二分是没错,只是最后计算错了。大概就是考虑二分出的最小长度是(k),对于一个大于(k)的长度(l)有好多个,没法知道(l)们到底要算到几,也就是说(7)中一定包含着(3/5/7),但是对于(l),其中可能有拆出(7,5)来的,也有可能有拆出(7,5,3)来的,无法同一个长度一起算。

    以下是错误代码:

    int ns[MAXN], base[MAXN], buc[MAXN], ed[MAXN] ; 
    int N, L = -1 ; LL K, res, fact = 1, _up ; char In[MAXN] ;
    
    il LL expow(LL x, LL y){
    	LL ret = 1 ; 
    	while (y){
    		if (y & 1)
    			(ret *= x) %= Mod ; 
    		(x *= x) %= Mod, y >>= 1 ; 
    	}
    	return ret ; 
    }
    il bool check(int p){
    	LL ret = 0 ;
    	int x = ed[p], i ; 
    	if (x % 2 == 0) return 0 ; 
    	for (int i = 1 ; i < p ; ++ i)
    		ret += 1ll * ((ed[i] - ed[p] + 2) / 2) * 1ll * buc[ed[i]] ; 
    	return (bool)(ret + (buc[ed[p]]) >= K) ;
    }
    int main(){
    	cin >> N >> K >> (In + 1) ;
    	ns[++ L] = '$', ns[++ L] = '#' ; int id = 0, rt = 0 ;
    	for (int i = 1 ; i <= N ; ++ i) ns[++ L] = (int)In[i], ns[++ L] = '#' ;
    	for (int i = 1 ; i <= L ; ++ i){
    		if (rt <= i) base[i] = 1 ;
    		else base[i] = min(base[2 * id - i], rt - i + 1) ;
    		while (ns[i - base[i]] == ns[i + base[i]]) ++ base[i] ; 
    		if (rt <= i + base[i] - 1) rt = i + base[i] - 1, id = i ; 
    	} 
    	for (int i = 1 ; i <= 2 * N + 2 ; ++ i) buc[base[i] - 1] ++ ;	
    	for (int i = 1 ; i < MAXN ; i += 2) if (buc[i]) ed[++ tot] = i ; 
    	reverse(ed + 1, ed + tot + 1) ; int L = 1, R = tot, Mid, ans, pos = 0 ; 
    	while (L <= R){
    		Mid = (L + R) >> 1 ; 
    		/*if (rand() % 2)
    			while (ed[Mid] % 2 == 0 && Mid < R) ++ Mid ;
    		else 
    			while (ed[Mid] % 2 == 0 && Mid > L) -- Mid ;*/
    		if (check(Mid)) ans = Mid, R = Mid - 1 ; else L = Mid + 1 ; 
    	}
    	pos = ans, res = 1ll, _up = ed[1] ;
    	for (int i = ed[pos] ; i <= ed[1] ; i += 2) fact *= 1ll * i ; 
    	for (int i = 1 ; i <= pos ; ++ i){
    		while (ed[i] < _up) fact /= _up, _up -= 2 ; 
    		if ((ed[i] - ed[pos] + 2) / 2 > K){
    			int j = 0 ; 
    			while (ed[i] >= ed[pos] && j < K)
    				res = res * 1ll * ed[i], ed[i] -= 2, ++ j ; 
    			break ; 
    		} 
    		res = res * expow(fact, buc[ed[i]] <= K ? buc[ed[i]] : K) % Mod ;
    		K -= 1ll * ((ed[i] - ed[pos] + 2) / 2) * 1ll * buc[ed[i]] ; 
    		if (K <= 0) break ; 
    	} 	 
    	cout << res << endl ; return 0 ; 
    }
    
    

    观察失误点,贡献无法提前计算,那么可以考虑延后计算,这样一定能保证准确凑出来(K).

    int tot, ns[MAXN], base[MAXN], buc[MAXN], ed[MAXN] ; 
    int N, L ; LL K, res, fact = 1, _up ; char In[MAXN] ;
    
    il LL expow(LL x, LL y){
    	LL ret = 1 ; 
    	while (y){
    		if (y & 1)
    			(ret *= x) %= Mod ; 
    		(x *= x) %= Mod, y >>= 1 ; 
    	}
    	return ret ; 
    }
    int main(){
    	cin >> N >> K >> (In + 1) ;
    	ns[++ L] = '$', ns[++ L] = '#' ; int id = 0, rt = 0 ;
    	for (int i = 1 ; i <= N ; ++ i) ns[++ L] = (int)In[i], ns[++ L] = '#' ;
    	for (int i = 1 ; i <= L ; ++ i){
    		if (rt <= i) base[i] = 1 ;
    		else base[i] = min(base[2 * id - i], rt - i) ;
    		while (ns[i - base[i]] == ns[i + base[i]] && i + base[i] <= L && i - base[i] >= 1) ++ base[i] ; 
    		if (rt <= i + base[i] - 1) rt = i + base[i], id = i ; 
    	}
    	for (int i = N ; i >= 1 ; -- i){
    		ans += buc[i] ; 
    		if (!(i & 1)) continue ;		
    		if (ans <= K)	
    			(res *= expow(i, ans)) %= Mod, K -= ans ; 
    		else { (res *= expow(i, K)) %= Mod, K -= ans ; break ; }
    	} 
    	cout << res << endl ;
    	return 0 ; 
    }
    

    。。。这题一开始写挂了,然后两天后(就是写这行字的时候)整理这道题,又调了半天才发现为啥二分不对…qaq脑子是个好东西。

    (2) LG4555 [国家集训队]最长双回文串

    这题比第一题友善了很多。。。

    输入长度为(n)的串(S),求(S)的最长双回文子串(T),即可将(T)分为两部分(X)(Y),((|X|,|Y|≥1))且(X)(Y)都是回文串。

    嗯,其实就是求以每个点为右端点/左端点的最长回文串长度。用Manacher做的话,就是一开始先推出以每个点为轴的最长回文串长度,然后用这个去更新每端点。注意到这么做有些包含在原来求出的最长回文串内部的小回文串可能并不可以求出来,于是再dp一遍即可。

    	cin >> (In + 1), N = strlen(In + 1) ; 
    	int i, id = 0, rt = 0 ; ns[++ L] = '$', ns[++ L] = '#' ; 
    	for (i = 1 ; i <= N ; ++ i) ns[++ L] = (int)In[i], ns[++ L] = '#' ;
    	for (i = 1 ; i <= L ; ++ i){
    		if (rt <= i) base[i] = 1 ; 
    		else base[i] = min(rt - i + 1, base[2 * id - i]) ;  
    		while (ns[i + base[i]] == ns[i - base[i]]) base[i] ++ ;
    		if (i + base[i] > rt) rt = i + base[i] - 1, id = i ; 
    	}
    //	for (i = 1 ; i <= L ; ++ i) cout << base[i] << " " ; puts("") ;
    //	for (i = 1 ; i <= L ; ++ i) cout << (char)ns[i] << " " ;
    	for (i = 1 ; i <= L ; ++ i){
    		int l = i / 2 - (base[i] / 2) + 1 ;
    		int r = i / 2 + (base[i] / 2) - 1 ; if (ns[i] == 35) ++ r ;
    		Ls[r] = max(Ls[r], base[i] - 1), Rs[l] = max(Rs[l], base[i] - 1) ;
    	}
    	for (i = 1 ; i <= N ; ++ i) Ls[i] = max(Ls[i], Ls[i + 1] - 2) ;
    	for (i = 1 ; i <= N ; ++ i) Rs[i] = max(Rs[i], Rs[i - 1] - 2) ;
    //	for (i = 1 ; i <= N ; ++ i) cout << Ls[i] << " " ; puts("") ;
    //	for (i = 1 ; i <= N ; ++ i) cout << Rs[i] << " " ;
    	for (i = 1 ; i < N ; ++ i) ans = max(ans, Ls[i] + Rs[i + 1]) ; cout << ans << endl ; return 0 ;
    }
    
  • 相关阅读:
    代码、复制[置顶] 跑起来,第一个sshby小雨
    返回、参数了解Javascript函数:parseInt()by小雨
    代码、复制Javascript执行效率小结by小雨
    图片、JQuery学习笔记(图片的展开和伸缩)by小雨
    登录、项目Spring mvc Session拦截器by小雨
    社区、标签jsp中获取状态怎么写?by小雨
    升级、地方LKDBHelper 使用FMDB 对数据库的自动操作by小雨
    广播、应用Android BroadcastReceiver(一)by小雨
    调试、手机手游开发知识(三)--NDK联机调试by小雨
    设置、数值【Cocos2DX 】初窥门径(11)CCUserDefault:保存数据by小雨
  • 原文地址:https://www.cnblogs.com/pks-t/p/12030976.html
Copyright © 2011-2022 走看看