蓝桥杯 小蓝的价值之旅
题意
给定长为\(n\)的字符串 , 每个字母都有\(val\)值 , 计算其所有子串的总价值
当字串价值和模\(k\)为区间长度时 , 产生价值\(m\)
思路
典型的数组求值问题 , 这种问题一般想双指针 , 预处理 , 枚举贡献 , 枚举左右端点 , 但是都没有什么思路
考虑模数这个特点 , 一般来说在模小时可以考虑枚举模来作为思路切入点 , 然后发现似乎也不行
考虑前缀和 , 如果说\(s_i-s_j\)模k为\(i-j\), 该段可行(左开右闭)
即 $$sum_r - sum_{l-1} mod k = r - l + 1$$
进一步化简为:
\[sum_r - r mod k = sum_{l-1} - (l-1) mod k
\]
因此维护\(sum_i - i\)即可 , 如果当前枚举的\(i\)的\(sum_i - i\)值已经在之前出现过 , 那么每一次都可以作为一个新的贡献
需要\(sum_0 - 0 = 0\)吗 ? 需要的 , 实际上相当于从头开始的一段序列
需要注意 , 区间长度不能大于等于k
拾取新思路碎片 : 当出现连续数据和 , 考虑
前缀和
拾取新思路碎片 : 当出现序列相关算式 , 考虑将其规约成只有一个变量形式
代码
## 题意
给定长为$n$的字符串 , 每个字母都有$val$值 , 计算其所有子串的总价值
当字串价值和模$k$为区间长度时 , 产生价值$m$
## 思路
典型的数组求值问题 , 这种问题一般想双指针 , 预处理 , 枚举贡献 , 枚举左右端点 , 但是都没有什么思路
考虑**模数**这个特点 , 一般来说在模小时可以考虑枚举模来作为思路切入点 , 然后发现似乎也不行
考虑前缀和 , 如果说$s_i-s_j$模k为$i-j$, 该段可行(左开右闭)
即 $$sum_r - sum_{l-1} mod k = r - l + 1$$
进一步化简为:
$$sum_r - r mod k = sum_{l-1} - (l-1) mod k$$
因此维护$sum_i - i$即可 , 如果当前枚举的$i$的$sum_i - i$值已经在之前出现过 , 那么每一次都可以作为一个新的贡献
需要$sum_0 - 0 = 0$吗 ? 需要的 , 实际上相当于从头开始的一段序列
需要注意 , **区间长度不能大于等于k**
#### 拾取新思路碎片 : 当出现连续数据和 , 考虑
前缀和
#### 拾取新思路碎片 : 当出现序列相关算式 , 考虑将其规约成只有一个变量形式
## 代码
```cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {
int ans = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
int n,k,m;
int w[30];
const int N =1e5+10;
int sum[N],val[N],cnt[N];
signed main() {
n=read(),k=read(),m=read();
for (int i = 0; i< 26; i++) {
w[i] =read();
w[i] %= k;
}
string s;
cin>>s;
s = " " + s;
int ans = 0;
cnt[0]=1;
for (int i = 1; i< s.size(); i++) {
sum[i] = (sum[i-1] + w[s[i]-'a'] )% k;
val[i] = ((sum[i] - i)%k + k) % k;
if (i>=k) {
cnt[val[i-k]]--;
}
ans += cnt[val[i]];
cnt[val[i]]++;
}
cout<<ans * m<<"\n";
return 0;
}

浙公网安备 33010602011771号