T1 [JZOJ4879] 少女觉
题目描述
“在幽暗的地灵殿中,居住着一位少女,名为古明地觉。”
“据说,从来没有人敢踏入过那座地灵殿,因为人们恐惧于觉一族拥有的能力——读心。”
“掌控人心者,可控天下。”
人的记忆可以被描述为一个黑块 $B$ 与白块 $W$ 的序列,其中情感值被定义为序列中黑块数量与白块数量之比。
小五口在发动读心术时,首先要解析人的记忆序列,因此,需要将序列分割为一些段,并且要求每一段记忆序列的情感值都相等。
下面给出两个例子:
BWWWBB -> BW + WWBB $(Ratio=1:1)$
WWWBBBWWWWWWWWWB -> WWWB + BBWWWWWW + WWWB $(Ratio=3:1)$
现在小五手上有一个人的记忆序列,她想要知道,如何将手中的记忆序列分成尽可能多的段呢?
数据范围
对于 $10 \%$ 的数据,$N \leq 15$
对于 $20 \%$ 的数据,$N \leq 500$
另有 $30 \%$ 的数据,$K=1$
另有 $30 \%$ 的数据,$K \leq 50$
对于 $100 \%$ 的数据,$N \leq 10^5$,序列长度不超过 $10^9$
分析
显然每段序列的黑白块之比都等于总序列的黑白块之比
所以只要在每加入一段相同颜色的连续方块时,判断是否能组成一段新的合法序列
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 100005 int T, n, ans; int s[N][2], sum[2], last[2]; char c; int gcd(int a,int b) { return !b ? a : gcd(b, a % b); } int main() { freopen("silly.in", "r", stdin); freopen("silly.out", "w", stdout); scanf("%d", &T); while (T--) { ans = sum[0] = sum[1] = last[0] = last[1] = 0; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d %c", &s[i][1], &c); if (c == 'B') s[i][0] = 0, sum[0] += s[i][1]; if (c == 'W') s[i][0] = 1, sum[1] += s[i][1]; } if (!sum[0]) {printf("%d\n", sum[1]); continue;} if (!sum[1]) {printf("%d\n", sum[0]); continue;} int g = gcd(sum[0], sum[1]); sum[0] /= g; sum[1] /= g; for (int i = 1; i <= n; i++) { int now = s[i][0]; if (!last[now ^ 1] || last[now ^ 1] % sum[now ^ 1]) { last[now] += s[i][1]; continue; } int need = last[now ^ 1] / sum[now ^ 1] * sum[now] - last[now]; if (need < 0) last[now] += s[i][1]; else if (s[i][1] < need) last[now] += s[i][1]; else last[now] = s[i][1] - need, last[now ^ 1] = 0, ans++; } printf("%d\n", ans); } return 0; }