Codeforces 1660F Promising String (hard version) 找规律+前缀和
Primo Pan

题面

F2. Promising String (hard version) time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output This is the hard version of Problem F. The only difference between the easy version and the hard version is the constraints.

We will call a non-empty string balanced if it contains the same number of plus and minus signs. For example: strings “+–+” and “++-+–” are balanced, and strings “+–”, “–” and “” are not balanced.

We will call a string promising if the string can be made balanced by several (possibly zero) uses of the following operation:

replace two adjacent minus signs with one plus sign.
In particular, every balanced string is promising. However, the converse is not true: not every promising string is balanced.

For example, the string “-+—“ is promising, because you can replace two adjacent minuses with plus and get a balanced string “-++-“, or get another balanced string “-+-+”.

How many non-empty substrings of the given string 𝑠 are promising? Each non-empty promising substring must be counted in the answer as many times as it occurs in string 𝑠.

Recall that a substring is a sequence of consecutive characters of the string. For example, for string “+-+” its substring are: “+-“, “-+”, “+”, “+-+” (the string is a substring of itself) and some others. But the following strings are not its substring: “–”, “++”, “-++”.

Input
The first line of the input contains an integer 𝑡 (1≤𝑡≤104) —the number of test cases in the test.

Then the descriptions of test cases follow.

Each test case of input data consists of two lines. The first line consists of the number 𝑛 (1≤𝑛≤2⋅105): the length of 𝑠.

The second line of the test case contains the string 𝑠 of length 𝑛, consisting only of characters “+” and “-“.

It is guaranteed that the sum of values 𝑛 over all test cases does not exceed 2⋅105.

Output
For each test case, print a single number: the number of the promising non-empty substrings of string 𝑠. Each non-empty promising substring must be counted in the answer as many times as it occurs in string 𝑠.

Example
inputCopy
5
3
+-+
5
-+—
4


7
–+—+
6
+++—
outputCopy
2
4
2
7
4
Note
The following are the promising substrings for the first three test cases in the example:

𝑠[1…2]=”+-“, 𝑠[2…3]=”-+”;
𝑠[1…2]=”-+”, 𝑠[2…3]=”+-“, 𝑠[1…5]=”-+—“, 𝑠[3…5]=”—“;
𝑠[1…3]=”—“, 𝑠[2…4]=”—“.

题目大意

给你一个长度为n,只包含+-符号的字符串。连续两个-可以合并成一个+。问你该字符串中有多少子串满足,经过一些合并(或没有合并)后子串中+-号数量相等。

题解

上结论: 子串中-的个数比+多,且差值是3的倍数,则记一次贡献。

首先根据狄利克雷原理(抽屉原理),如果-的个数比+多于2个即一定有两个-相连。并且进行一次操作后,+-号的差异度会+3。那么就可以进行很多次操作将差异度变为0。

这题有小数据,那么对于小数据,O(n^2)的暴力为(假设一开始差异度为n)。

1
2
3
4
5
6
7
8
9
cur=n;
for (int i=0;i<n;i++)
{
cur+=(s[i]=='+')?1:-1;
dp[i]=cur;
for (int j=0;j<i;j++)
if (s[j]>s[i] && (s[j]-s[i])%3==0)
ans++;//[j,i]为合理区间
}

考虑优化这个的暴力,如果把cur模3 ,那么当前右端点的贡献就是左侧所有比它大的%3剩余系的值。看到很多人写了一个log级的数据结构,但观察到差异度最大值为2n-1,不是很大,所以直接可以开个表记录一下,O(n)查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
inline void solve()
{
int n;
cin>>n;
string s;
cin>>s;
int lim=2*n+1;
int lst=n;
vector<int> f (3,0);
vector<int> cnt (lim,0);
f[lst%3]++;
cnt[lst]++;
ll ans=0;
for (int i=0;i<n;i++)
{
int cur=lst;
if (s[i]=='+')
{
f[cur%3]-=cnt[cur];//比cur小的不能用了
cur++;
ans+=f[cur%3];
f[cur%3]++;
cnt[cur]++;
}
else{
cur--;
f[cur%3]+=cnt[cur];//小于等于cur的都能用
ans+=f[cur%3];
f[cur%3]++;
cnt[cur]++;
}
lst=cur;
}
cout<<ans<<endl;
}