题意:
字符串的子串当中每个字母的出现个数(用 s [ ′ a ′ ] [ l , r ]来表示),要求的就是每个字母的所有 s [ l , r ] s[l, r]s[l,r] 的平方和
分析:
首先我们用dp[i]表示以i做结尾的贡献:
之后我们来分析:每次多加上当前字符 会多造成多少贡献:
以ababaa为例 比样例多了个a
?和/表示没有字符 对起作用
??/??a 贡献是
1
2
1^2
12
??/?aa 贡献是
2
2
2^2
22
???baa 贡献是
2
2
+
1
2
2^2+1^2
22+12
??abaa 贡献是
3
2
+
1
1
3^2+1^1
32+11
?babaa 贡献是
2
2
+
3
2
2^2+3^2
22+32
ababaa 贡献是
2
2
+
4
2
2^2+4^2
22+42
对比一下前一位的a贡献,也就是ababa
??/?a 贡献是
1
2
1^2
12
???ba 贡献是
1
2
+
1
2
1^2+1^2
12+12
??aba 贡献是
2
2
+
1
1
2^2+1^1
22+11
?baba 贡献是
2
2
+
2
2
2^2+2^2
22+22
ababa 贡献是
2
2
+
3
2
2^2+3^2
22+32
我们相加一下,看看都多了那些东西:
??/??a 贡献是
1
2
1^2
12
??/?aa 多贡献是
2
2
−
1
2
2^2-1^2
22−12
???baa 多贡献是
2
2
+
1
2
−
1
2
−
1
2
2^2+1^2-1^2-1^2
22+12−12−12
??abaa 多贡献是
3
2
+
1
1
−
2
2
−
1
2
3^2+1^1-2^2-1^2
32+11−22−12
?babaa 多贡献是
2
2
+
3
2
−
2
2
−
2
2
2^2+3^2-2^2-2^2
22+32−22−22
ababaa 多贡献是
2
2
+
4
2
−
3
2
−
2
2
2^2+4^2-3^2-2^2
22+42−32−22
整合下
1
2
+
(
2
2
−
1
2
)
+
(
2
2
−
1
2
)
+
(
3
2
−
2
2
)
+
(
3
2
−
2
2
)
+
(
4
1
−
3
2
)
1^2+(2^2-1^2)+(2^2-1^2)+(3^2-2^2)+(3^2-2^2)+(4^1-3^2)
12+(22−12)+(22−12)+(32−22)+(32−22)+(41−32)
然后我们为一位就是当前位上会有1贡献 因为有6项所以加上6 对上面先剪掉6看一下:
说后面的时候 先看平方差公式:
(
a
+
1
)
2
−
a
2
(a+1)^2-a^2
(a+1)2−a2= (a+a+1)=2a+1
上面说我们对每项-1,所以变成
(
2
2
−
1
2
−
1
)
+
(
2
2
−
1
2
−
1
)
+
(
3
2
−
2
2
−
1
)
+
(
3
2
−
2
2
−
1
)
+
(
4
1
−
3
2
−
1
)
(2^2-1^2-1)+(2^2-1^2-1)+(3^2-2^2-1)+(3^2-2^2-1)+(4^1-3^2-1)
(22−12−1)+(22−12−1)+(32−22−1)+(32−22−1)+(41−32−1)
再根据我们上面的平方差公式得:
2
∗
1
+
2
∗
1
+
2
∗
2
+
2
∗
3
2*1+2*1+2*2+2*3
2∗1+2∗1+2∗2+2∗3 也就是2*前面这个字符的位置,所以我们只需要记录前面字符位置和即可。然后加上当前字符位置。
递归方程就为:
d
p
[
i
]
=
d
p
[
i
−
1
]
+
2
∗
s
u
m
[
n
u
m
]
+
i
dp[i]=dp[i-1]+2*sum[num]+i
dp[i]=dp[i−1]+2∗sum[num]+i
num表示当前字符。sum表示前面字符位置和。
void solve() {
int t;
cin >> t;
while (t--) {
memset(cnt, 0, sizeof(cnt));
memset(sum, 0, sizeof(sum));
scanf("%s", s + 1);
int n = strlen(s + 1);
ll ans = 0;
for (int i = 1; i <= n; i++) {
int now = s[i] - 'a' + 1;
dp[i] = (dp[i - 1] + 2 * sum[now] + i) % mod;
sum[now] = (sum[now] + i) % mod;
ans = (ans + dp[i]) % mod;
cout<<dp[i]<<" ";
}
printf("%lld\n", ans);
}
}