# HDU7055题解(平方=1+3+5+7+9+。。。的思路小技巧)

传送门

题意

​ 给你一个 \(10^5\) 的字符串 \(s\)。我们定义一个函数 \(sqc(s,i,j,c)\) 表示字符 \(c\)\(s\) 的子串 \([i,j]\) 中出现的次数。

​ 让你求:

\[\sum_{c=97}^{122}{\sum_{i=1}^{n}{\sum_{j=i}^{n}{sqc(s,i,j,c)^{2}}}}​ \]

​ 其实没有 \(sqc\) 的平方问题就很好处理,但是平方特别恶心,考虑转化平方。

​ 考虑平方的性质,我们发现对于任何一个完全平方数,我们总能将其表示为 \(1+3+5+7+\cdots+2n-1\) 的形式。因为本问题中每个字符是两两独立的。假定我们现在在位置 \(i\),字符是 \(c\),考虑 \(c\) 的贡献。设 \([l,r]\) 是某个包含位置 \(i\) 的区间,根据我们刚才对平方的转化,只要我们知道现在这个 \(c\) 在该区间中是第几个,我们就可以算出这个位置对这个区间的贡献。

​ 我们现在就可以从 \(1\)\(n\) 枚举字符串 \(s\)。假定我们在第 \(i\) 个位置,字符为 \(c\)。我们不难发现,因为我们要考虑当前位置的贡献,它对任何一个包含当前位置的区间的贡献只和它是从左往右数第几个c有关,只要右端点 \(r\ge i\) 对于每个 \(r\) 贡献都是一样的,我们一会就可以算出符合条件的左端点的贡献和,再 \(\times (n-r+1)\) 就行了。

​ 那么假定 \(r\) 已经确定了,我们考虑如何确定l的贡献和,考虑开一个数组 \(sum[c]\) ,记录此时左端点为1到 \(i\) 的所有的贡献。

​ 那么怎么转移呢?因为字母间独立,设上一个 \(c\)\(j\) ,对于某个 \(l\)

  • 如果它在 \([j+1,i]\),贡献是 \(1\)
  • \(j\) 对它的贡献是 \(1\)\(i\) 就是 \(3\)
  • \(j\) 对他的贡献是 \(3\)\(i\) 就是 \(5\)
  • \(\vdots\)

​ 那么我们发现,如果先把 \(sum[c]+i\),然后统计 \(ans\),再 \(sum[c]+i\) 就是对的,细推一下就可以验证。

​ 具体实现就看代码了。

#include <bits/stdc++.h>
using namespace std ;
#define ll long long
#define rep(i,l,r) for(ll i=(l);i<=(r);++i)
#define per(i,r,l) for(ll i=(r);i>=(l);--i)
#define wif while
#define mem(a,b) memset(a,b,sizeof a)
const ll inf = INT_MAX , df = 1e6 + 7 , mod = 998244353 ;
char st[df] ;
ll i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,sum[26],ans;
void solve()	{
	mem( sum , 0 ) , ans = 0 ;	scanf( "%s" , st + 1 ) , n = strlen( st + 1 ) ;
	rep(i,1,n)	{
		ll &x = sum[ st[i] - 'a' ] ;
		( x += i ) %= mod , ( ans += x * ( n - i + 1 ) % mod ) %= mod , ( x += i ) %= mod ;
	}
	printf("%lld\n",ans);	}
/*此题思路:平方拆分为1+3+5+7+9+...,对于某个字母,统计它是1还是3还是5...的方案数即可,具体实现如上*/
int main()	{
	ll t ;	scanf("%lld",&t);
	wif(t--)	solve() ;
}
posted @ 2021-08-11 19:53  red_giant_bear  阅读(230)  评论(0)    收藏  举报