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;
}
posted @ 2024-07-27 11:58  123wwm  阅读(24)  评论(0)    收藏  举报