CF1996E Decode
这题我竟然做出来了/jy
题目要求所有子区间的子区间的贡献之和,那么一个子区间 \([l,r]\) 的贡献就要乘上 \(l*(n-r+1)\),因为左端点在 \([1,l]\) 内,右端点在 \([r,n]\) 范围内的区间均包含该区间。
我们令 \(0\) 为 \(-1\),计算前缀和 \(s\),那么一个区间 \([l,r]\) 内 \(0\) 的个数等于 \(1\) 个个数等价于 \(s_r=s_{l-1}\)。我们考虑枚举右端点 \(r\),去数 \([0,r-1]\) 内有多少个 \(s_i\) 满足 \(s_i=s_r\)。所有的区间要乘上系数 \((i+1)*(n-r+1)\),故贡献之和为 \((n-r+1)\) 乘上所有满足的 \(i+1\) 之和。这里我们可以用数组 \(cnt_i\) 表示 \([0,i-1]\) 内满足 \(s_j=s_i\) 的 \(j+1\) 之和,实现的时候注意负数的情况。时间复杂度为 \(O(n)\)。
代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef vector <int> vi;
typedef pair <int, int> pii;
inline int rd() { int x = 0, f = 1; char c = getchar(); while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); while (isdigit(c)) x = (x<<3)+(x<<1)+(c^48), c = getchar(); return x*f; }
template <typename T> inline void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x/10); putchar(x%10+48); }
const int mod = 1e9+7;
char c[200005];
void solve() {
cin >> (c+1);
int n = strlen(c+1), ans = 0; vi s(n+1), cnt(n*2+5);
for (int i = 1; i <= n; ++i) {
s[i] = c[i] == '0' ? -1 : 1;
s[i] += s[i-1];
}
cnt[n] = 1;
for (int i = 1; i <= n; ++i) {
ans = (ans+1ll*(n-i+1)*cnt[s[i]+n]%mod)%mod;
cnt[s[i]+n] = (cnt[s[i]+n]+i+1)%mod;
}
cout << ans << '\n';
}
int main() {
int t = rd();
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号