zoukankan      html  css  js  c++  java
  • Solution -「NOI.AC 省选膜你赛」T2

      这道题就叫 T2 我有什么办法www

    题目

    题意简述

      给定一个字符串 (s),其长度为 (n),求无序子串对 ((u,v)) 的个数,其中 ((u,v)) 满足 (u,v) 均为回文串且出现位置相交。

    数据规模

      (nle2 imes10^6),字符集为小写字母(于是测试数据里有神奇的'{'字符

    题解

      难得的水题呐!

      正难则反,首先求出总的回文子串对数,再减去出现位置不交的对数。

      对于前者,用 Manacher 或者 PAM 都可以轻松求出,这里用的 PAM。

      对于后者,记 (f(i)) 为原串中以 (i) 结尾的回文串个数,(g(i))(s[i..n]) 中的回文子串个数。那么不交的回文子串对的对数为:

    [sum_{i=1}^{n-1}f(i)g(i+1) ]

      (f)(g) 亦能用 PAM 求出,这道题就解决啦~

      最后测试数据出锅,AC 失败qwq。

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    const int MAXN = 2e6, MOD = 998244353, INV2 = 499122177;
    char s[MAXN + 5];
    int preend[MAXN + 5], sufend[MAXN + 5];
    
    class PalindromeAutomaton {
    private:
    	int cnt, lst, ch[MAXN + 5][26], len[MAXN + 5], link[MAXN + 5], dep[MAXN + 5];
    
    public:
    	PalindromeAutomaton (): cnt ( 1 ), lst ( 1 ), len { 0, -1 }, link { 1, 0 } {}
    	inline void clear () {
    		for ( int i = 0; i <= cnt; ++ i ) {
    			dep[i] = link[i] = len[i] = 0;
    			for ( int j = 0; j < 26; ++ j ) ch[i][j] = 0;
    		}
    		cnt = lst = 1, len[1] = -1, link[0] = 1;
    	}
    	inline int build ( const char* str, int* endcnt ) {
    		int ret = 0;
    		for ( int i = 1; str[i]; ++ i ) {
    			int cid = str[i] - 'a', p = lst;
    			for ( ; str[i] ^ str[i - len[p] - 1]; p = link[p] );
    			if ( ! ch[p][cid] ) {
    				int cur = ++ cnt, q = link[p]; len[cur] = len[p] + 2;
    				for ( ; str[i] ^ str[i - len[q] - 1]; q = link[q] );
    				dep[cur] = dep[link[cur] = ch[q][cid]] + 1, ch[p][cid] = cur;
    			}
    			ret = ( ret + ( endcnt[i] = dep[lst = ch[p][cid]] ) ) % MOD;
    		}
    		return ret;
    	}
    } pam;
    
    int main () {
    	scanf ( "%s", s + 1 );
    	int ans = pam.build ( s, preend ), n = strlen ( s + 1 );
    	ans = ( ans * ( ans - 1ll ) % MOD * INV2 % MOD + MOD ) % MOD;
    	pam.clear (), std :: reverse ( s + 1, s + n + 1 );
    	pam.build ( s, sufend ), std :: reverse ( sufend + 1, sufend + n + 1 );
    	for ( int i = n - 1; i; -- i ) sufend[i] = ( sufend[i + 1] + sufend[i] ) % MOD;
    	for ( int i = 1; i < n; ++ i ) ans = ( ( ans - 1ll * preend[i] * sufend[i + 1] % MOD ) % MOD + MOD ) % MOD;
    	printf ( "%d
    ", ans );
    	return 0;
    }
    
  • 相关阅读:
    经典分水岭算法的 C++ 实现
    一个简易的键盘按键测试程序
    工程优化方法中的“最速下降法”和“DFP拟牛顿法”的 C 语言实现
    基于哈夫曼编码的压缩解压程序(C 语言)
    博客选择:博客园 or CSDN
    Spring总结
    CSS总结
    EL表达式总结
    Jdbc总结
    hibernate总结
  • 原文地址:https://www.cnblogs.com/rainybunny/p/13099628.html
Copyright © 2011-2022 走看看