Codeforces 930B Game with String

Codeforces930B游戏字符串策略
本文探讨了Codeforces930B题目中的一种游戏策略,即通过分析特定字符串及其循环移位来确定玩家获胜的概率。文章提供了一个算法解决方案,通过枚举不同字母开头及各种可能的区间长度来找出唯一确定移位的情况。

传送门:Codeforces 930B


B. Game with String
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Vasya and Kolya play a game with a string, using the following rules. Initially, Kolya creates a string s, consisting of small English letters, and uniformly at random chooses an integer k from a segment [0, len(s) - 1]. He tells Vasya this string s, and then shifts it k letters to the left, i. e. creates a new string t = sk + 1sk + 2... sns1s2... sk. Vasya does not know the integer k nor the string t, but he wants to guess the integer k. To do this, he asks Kolya to tell him the first letter of the new string, and then, after he sees it, open one more letter on some position, which Vasya can choose.

Vasya understands, that he can't guarantee that he will win, but he wants to know the probability of winning, if he plays optimally. He wants you to compute this probability.

Note that Vasya wants to know the value of k uniquely, it means, that if there are at least two cyclic shifts of s that fit the information Vasya knowns, Vasya loses. Of course, at any moment of the game Vasya wants to maximize the probability of his win.

Input

The only string contains the string s of length l (3 ≤ l ≤ 5000), consisting of small English letters only.

Output

Print the only number — the answer for the problem. You answer is considered correct, if its absolute or relative error does not exceed 10 - 6.

Formally, let your answer be a, and the jury's answer be b. Your answer is considered correct if 

Examples
input
Copy
technocup
output
1.000000000000000
input
Copy
tictictactac
output
0.333333333333333
input
Copy
bbaabaabbb
output
0.100000000000000
Note

In the first example Vasya can always open the second letter after opening the first letter, and the cyclic shift is always determined uniquely.

In the second example if the first opened letter of t is "t" or "c", then Vasya can't guess the shift by opening only one other letter. On the other hand, if the first letter is "i" or "a", then he can open the fourth letter and determine the shift uniquely.


分析:概率题,比赛时候没认真看题,觉得比较难直接跳过了QAQ

枚举不同字母开头,区间长度从0到len-1的所有情况,若只出现了一次则记录下来,累加即可

具体实现见代码


代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 5000+5;
char str[maxn*2];
int a[26][26][maxn];
int tot,n;

int main(){
    scanf("%s",str+1);
    n = strlen(str+1);
    for (int i=1; i<=n; i++) str[i+n] = str[i];
    memset(a,0,sizeof(a));
    for (int i=1; i<=n; i++)
        for (int j=i+1; j<i+n; j++) a[str[i]-'a'][str[j]-'a'][j-i+1]++;

    tot = 0;
    for (int i=0; i<26; i++) {
        int tmp = 0;
        for (int j=1; j<=n; j++) {
            int sum = 0;
            for (int k=0; k<26; k++) if (a[i][k][j]==1) sum++;
            tmp = max(sum,tmp);
        }
        tot += tmp;
    }
    printf("%.14f\n",double(tot)/n);
    return 0;
}

<think> 题目:Codeforces 1679B - Stone Age Problem 题目大意:有一个数组,开始有n个元素,进行q次操作。操作有两种: 1. 将第i个元素的值改为x 2. 将数组中所有元素的值改为x 每次操作后,输出当前数组所有元素的和。 注意:操作1可能会在操作2之后发生,此时操作1会覆盖操作2对单个元素的赋值。 思路: 我们需要维护两个信息: 1. 当前数组每个元素的值(但注意,当有操作2时,我们并不真的立即更新所有元素,因为这样会超时) 2. 最后一次全局更新的时间戳和值 另外,我们还需要记录每个元素最后一次被单独更新的时间(时间戳),以判断该元素是否被之后的全局更新覆盖。 具体做法: 我们可以用一个数组arr来记录每个元素当前单独设置的值(即没有被全局覆盖的值),以及一个数组last_time记录每个元素最后一次单独更新的操作编号(或者时间戳,我们用操作序号作为时间戳)。 同时,我们维护一个全局的覆盖值base,以及最后一次全局更新的时间戳global_time(记录是第几次操作)。 还需要记录当前的和sum。 但是,注意操作2会重置整个数组,那么在此之后的单个更新会覆盖全局的值。因此,在执行操作1时,我们要检查这个元素上一次更新时间是否在最近一次全局更新之前,如果是,那么当前操作1时,这个元素之前的值实际上是全局覆盖的值,但我们在数组arr中保存的是单独设置的值,所以我们需要用全局覆盖的值来更新这个元素之前的值吗?其实不需要,因为我们只关心总和的变化。 具体操作: 我们设置: global_val: 最近一次全局赋值操作设置的值(如果没有,可以初始化为0?但是初始数组有初始值) last_global: 最近一次全局赋值操作的操作序号(从1开始) arr[i]: 记录第i个元素在单独赋值操作中设置的值(初始为初始数组的值) last[i]: 记录第i个元素最后一次单独赋值操作的操作序号(初始为0,表示没有单独赋值操作,或者如果一开始有初始值,我们可以认为是在第0次操作时由初始赋值赋予的,但注意初始赋值后没有全局操作,所以第一次操作前,每个元素的last[i]都是0,而last_global为0(表示没有全局操作)) 然后,在每次操作1(修改第i个元素为x)时: 首先,检查这个元素是否在最近一次全局操作之后被修改过?即比较last[i]和last_global: - 如果last[i] > last_global,说明该元素在最近一次全局操作之后被修改过,那么它当前的值就是arr[i]。 - 否则,该元素当前的值应该是全局值global_val。 然后,我们计算这个元素原来的值old_val,然后新的值变成x,那么总和的变化就是 (x - old_val) 更新总和:sum = sum + (x - old_val) 更新arr[i] = x,并且更新last[i] = current_op(当前操作序号) 在每次操作2(全局修改为x)时: 我们直接设置全局值:global_val = x 更新last_global = current_op(当前操作序号) 同时,总和变成 n * x 但是注意:我们不需要更新每个元素的arr和last,因为后续操作1会覆盖,而且我们通过时间戳来判断。 但是这里有一个问题:当前总和怎么得到?我们之前用sum记录,但是全局操作后,总和就是n*x。所以我们直接设置sum = n * x 然而,我们还需要注意:在全局操作之后,如果有操作1修改了某个元素,那么我们在操作1中已经更新了sum,并且更新了那个元素的值。但是全局操作时,我们设置sum为n*x,这相当于重置了整个数组。那么之后的操作1中,我们计算单个元素修改时,需要知道这个元素在修改前的值(可能是全局值,也可能是后来单独设置的值),这个我们已经通过last[i]和last_global的比较解决了。 但是,我们还需要维护sum。初始时,sum就是初始数组的和。 算法流程: 初始化: n, q 输入 arr: 初始数组,大小为n+1(1-indexed) last: 大小为n+1,初始全部为0 global_val = 0, last_global = 0 sum = 初始数组的和 然后进行q次操作,每次操作: 如果操作类型是1: 输入 i 和 x i = i(注意题目是1-indexed) 计算old_val: if last[i] > last_global: old_val = arr[i] else: old_val = global_val sum = sum - old_val + x arr[i] = x last[i] = current_op_index // 当前操作序号(从1到q) 输出 sum 如果操作类型是2: 输入 x global_val = x last_global = current_op_index sum = n * x 输出 sum 注意:这里我们不需要更新arr和last数组,因为后续操作1会更新,而操作2之后,所有元素的值都应该是x,但是我们通过时间戳机制在操作1中判断。 但是,这里有一个问题:在操作2之后,所有元素在逻辑上都被设置成了x。但是我们在操作1中只更新了被修改的元素,那么当我们再次遇到操作1时,对于没有被操作1修改的元素,我们仍然会通过last[i] <= last_global认为它是global_val(即最新的全局值),这样是正确的。 然而,我们还需要注意:全局操作后,我们重置了sum为n*x,这样在后续操作1中,我们计算单个元素变化时,这个元素在修改前的值我们通过全局值得到,这样是符合的。 但是,有一个问题:如果连续两次操作2,那么第一次操作2后,last_global被更新,sum被更新为n*x1。第二次操作2,我们更新last_global为当前操作序号,然后sum被更新为n*x2。这样也是正确的。 然而,在操作1中,我们更新了某个元素,然后后面又进行了一次操作2,那么该元素在操作2后就被覆盖了,所以它的值应该是操作2设置的值,直到下一次操作1修改它。 所以这个思路是可行的。 但是,我们如何维护每个元素的last[i]?我们只需要一个数组记录每个元素最后一次单独赋值的时间戳(操作序号)。另外,全局操作的时间戳用last_global记录。 注意:操作序号我们用当前操作的编号(从1到q)。 初始化: last_global = 0 // 表示还没有全局操作 global_val 初始值是多少?其实无所谓,因为一开始没有全局操作,每个元素的值由初始数组决定,所以一开始每个元素的值就是arr[i](初始值),并且last[i]=0(表示在第0次操作赋值,而last_global=0,所以比较时last[i](0)<= last_global(0),所以会取global_val?这不对!因为初始时没有全局操作,我们不应该取global_val。 因此,我们需要调整:初始时,我们设置last_global为0,global_val为0,但是初始数组的值是给定的,所以每个元素的值是arr[i]。那么,在第一次操作1之前,如果我们遇到操作1,我们希望取到初始值。那么我们可以这样:一开始,我们让last[i]都为0,而last_global为0,那么比较last[i] <= last_global?因为0<=0,所以会取global_val(0)?这显然不对。 所以我们需要修改:我们设定初始时,每个元素都有自己的值(初始值),并且它们都没有被全局操作覆盖?因此,我们可以设定last_global的初始值为-1(或者0,但是时间戳从1开始),然后初始时,每个元素的last[i]为0(表示在第0次操作时被赋值),而第0次操作(初始赋值)在全局操作之前(因为全局操作从第一次操作开始才可能有)。所以我们可以这样比较: if last[i] > last_global -> 使用arr[i] else -> 使用global_val 但是初始时,last_global为0(表示没有全局操作,或者全局操作在0次操作时?),而last[i]为0,那么0<=0,所以使用global_val(初始为0),但是初始值并不是0,而是arr[i](初始值)。 所以我们需要改变:在初始时,我们不应该使用global_val,而是使用初始数组的值。因此,我们可以认为初始数组的值是单独赋值的,并且时间戳为0。而全局操作的时间戳初始值设为-1(这样0>-1,所以会使用arr[i])。 因此,我们初始化: last_global = -1 // 表示还没有全局操作 global_val = 0 // 这个值无所谓,因为不会使用(除非有全局操作) last[i] = 0 // 表示在第0次操作(初始)被赋值 arr[i] = 初始数组的第i个值 sum = 初始数组的和 这样,在第一次操作1时(假设操作1是第一次操作): 对于元素i,last[i]=0,last_global=-1,0>-1为真,所以old_val=arr[i](初始值),然后更新arr[i]=x,last[i]=1(当前操作序号1),然后更新sum。 在第一次操作2(全局操作)时,我们设置last_global=1(当前操作序号),global_val=x,然后sum=n*x。 接下来,如果再来操作1(第二次操作1,即第三次操作): 对于元素i,last[i]可能是0(从未修改过)或者1(如果之前修改过),而last_global=1(第二次操作是操作2,操作序号2?不对,操作序号是顺序的,第一次操作是1,第二次操作是2,第三次操作是3)。 注意:操作序号我们按顺序从1到q。 假设是第一次操作1(操作序号1)修改了某个元素,那么last[i]=1,然后第二次操作是操作2(操作序号2),然后第三次操作是操作1(操作序号3)修改同一个元素。 那么,last[i]=1,last_global=2(因为第二次操作是操作2,我们设置last_global=2),因为1<2,所以old_val=global_val(即第二次操作2设置的x),然后更新为新的x3。 这样是正确的。 但是注意:在操作2之后,我们并没有更新每个元素的last[i](因为我们避免O(n)的更新),所以当操作1来的时候,对于那些没有被操作1修改的元素,它们的last[i]仍然小于last_global,所以会使用global_val。 因此,我们只需要维护: global_val, last_global arr: 每个元素单独赋值的值(初始为初始数组的值) last: 每个元素最后一次单独赋值的时间戳(初始为0) sum: 当前数组的和 注意:在操作2之后,我们更新sum为n*x,这可能会因为之前单独赋值操作而改变,所以操作2后我们就重置了整个数组。 但是,在操作2之后,我们并没有改变arr和last数组,那么当操作2之后,我们进行一个操作1修改一个元素,那么我们在操作1中更新了arr[i]和last[i],然后后续操作1再修改其他元素,这些元素还没有被单独修改过(即last[i]<=last_global),所以会使用global_val(即操作2设置的值)。 所以这个逻辑是自洽的。 但是,注意:我们如何维护sum?在操作1中,我们根据条件得到old_val,然后减去old_val,加上新值x,得到新的sum。在操作2中,我们直接将sum设为n*x。 但是,在多次操作2后,中间夹杂操作1,这样处理也是正确的吗? 例如: 操作1:修改某个元素,我们更新了arr[i]和last[i],并且更新了sum。 操作2:全局修改,设置global_val=x,last_global=当前操作序号,并且sum=n*x。 操作1:修改一个元素,此时这个元素在操作1修改前,因为last[i](上一次操作1的时间戳)小于操作2的时间戳,所以old_val=global_val(即操作2设置的值),然后更新为x,那么更新sum:sum = sum - global_val + x = n*x - global_val + x,但是global_val就是操作2设置的值,所以等于n*x - x + x = n*x + (x - global_val) ?不对,因为我们修改了一个元素,所以实际上应该是 (n-1)*x + x_new = n*x + (x_new - x) ?所以我们的计算:sum = sum - old_val + x = n*x - old_val + x,而old_val等于global_val(也就是x),所以等于 n*x - x + x = n*x,然后加上新值x再减去旧值x,实际上应该是n*x - old_val + x = n*x - x + x = n*x,这不对!因为我们修改了一个元素,新的和应该是 (n*x - x + x_new) = n*x - x + x_new。 所以,在操作1中,我们的计算应该是: old_val = (last[i] > last_global) ? arr[i] : global_val; sum = sum - old_val + x; 而操作2后,sum = n*x。 在操作2后的操作1:假设操作2设置x0,那么操作1修改第i个元素为x1,则: old_val = x0 sum = n*x0 - x0 + x1 = (n-1)*x0 + x1 而实际应该是:原来所有元素都是x0,现在修改一个为x1,所以总和确实是(n-1)*x0+x1。 因此,计算正确。 但是,在操作2后,我们修改了sum为n*x0,然后操作1:sum = n*x0 - x0 + x1 = (n-1)*x0+x1,正确。 所以我们可以这样实现。 注意:数据范围 n, q up to 200,000 每次操作1是O(1)的,操作2也是O(1)的,所以总复杂度O(q)。 代码实现: 步骤: #include <iostream> #include <vector> using namespace std; typedef long long ll; // 因为和可能很大 int main() { ll n, q; cin >> n >> q; vector<ll> arr(n+1); // 1-indexed vector<ll> last(n+1, 0); // 时间戳,初始为0 ll global_val = 0; ll last_global = -1; // 最后一次全局操作的操作序号,初始-1 ll sum = 0; for (ll i = 1; i <= n; i++) { cin >> arr[i]; sum += arr[i]; } for (ll op_index = 1; op_index <= q; op_index++) { ll type; cin >> type; if (type == 1) { ll i, x; cin >> i >> x; ll old_val; // 检查这个元素最后一次单独修改的时间戳和全局时间戳 if (last[i] > last_global) { old_val = arr[i]; } else { old_val = global_val; } // 更新sum sum = sum - old_val + x; // 更新arr[i]和last[i] arr[i] = x; last[i] = op_index; // 当前操作序号 cout << sum << endl; } else if (type == 2) { ll x; cin >> x; // 全局更新 global_val = x; last_global = op_index; sum = n * x; cout << sum << endl; } } return 0; } 但是,注意:在操作2之后,我们并没有修改arr和last数组。这样,后续操作1在访问arr[i]时,它保存的是上一次单独修改的值(可能是在全局操作之前修改的,也可能是全局操作之后修改的),但是我们并不需要这个值来代表当前值,因为我们通过时间戳机制已经得到了正确的old_val。而且,在操作1中,我们更新arr[i]为新的x,并更新last[i]为当前操作序号,这样这个元素就从全局操作中独立出来了。 所以这个代码是可行的。 但是,有一个边界:如果操作2之后,我们并没有对某个元素进行单独修改,那么它的last[i]就会一直小于last_global,所以每次操作1访问它时,都会使用global_val(即最近一次操作2的值)作为old_val。这样是正确的。 测试一下: 初始:n=3, arr=[1,2,3] -> sum=6 操作1:1 2 5 -> 修改第二个元素,原来第二个元素的值:因为last[2]=0,last_global=-1,0>-1为假?等等,0<=-1?不对,0>-1为真?所以old_val=arr[2]=2。然后更新为5,sum=6-2+5=9,输出9。然后arr[2]=5, last[2]=1。 操作2:2 1 -> 全局设置为1,last_global=2(当前操作序号2),global_val=1,sum=3*1=3,输出3。 操作1:1 2 3 -> 修改第二个元素,此时last[2]=1,last_global=2,1<2,所以old_val=global_val=1。然后更新:sum=3-1+3=5,arr[2]=3,last[2]=3,输出5。 操作1:1 1 1 -> 修改第一个元素,last[1]=0(初始),last_global=2,0<2,所以old_val=1。然后更新:sum=5-1+1=5,arr[1]=1,last[1]=4,输出5。 最后数组:第一个元素1(在操作4被修改),第二个元素3(在操作3被修改),第三个元素呢?没有修改,所以应该是全局值1?但是操作2后全局值为1,然后没有操作1修改第三个元素,所以第三个元素应该是1。所以总和=1+3+1=5,正确。 因此,代码按照上述编写。 注意:long long,因为n,q都是200000,元素值最大10^9,所以和最大200000*10^9=2e15,long long最大9e18,所以用long long足够。 提交到Codeforces,注意使用C++17,并且注意输入输出效率。 我们使用cin和cout,但是数据量20万,所以可以关闭同步: ios::sync_with_stdio(false); cin.tie(0); 完整代码: </think> 以下是解决 Codeforces 1679B 问题的 C++ 实现。该问题要求在两种操作下动态维护数组的和: 1. **单点修改**:将第 `i` 个元素改为 `x` 2. **全局修改**:将所有元素改为 `x` ### 解决方案 我们使用以下策略高效处理操作: - **时间戳机制**:记录每个元素**最后一次单独修改**的操作编号(`last[i]`) - **全局状态**:记录**最后一次全局修改**的操作编号(`last_global`)和值(`global_val`) - **动态和**:维护当前数组的和(`sum`),在每次操作后更新 ```cpp #include <iostream> #include <vector> using namespace std; typedef long long ll; int main() { ios::sync_with_stdio(false); cin.tie(0); ll n, q; cin >> n >> q; vector<ll> arr(n + 1); vector<ll> last(n + 1, 0); // 初始时间戳为0 ll global_val = 0; ll last_global = -1; // 初始无全局操作 ll sum = 0; // 读入初始数组 for (ll i = 1; i <= n; i++) { cin >> arr[i]; sum += arr[i]; } // 处理q次操作 for (ll op_index = 1; op_index <= q; op_index++) { ll type; cin >> type; if (type == 1) { ll i, x; cin >> i >> x; // 判断当前元素的旧值 ll old_val; if (last[i] > last_global) { old_val = arr[i]; // 最近有单独修改 } else { old_val = global_val; // 最近被全局修改覆盖 } // 更新和、数组值及时间戳 sum = sum - old_val + x; arr[i] = x; last[i] = op_index; cout << sum << '\n'; } else if (type == 2) { ll x; cin >> x; // 更新全局状态并重置和 global_val = x; last_global = op_index; sum = n * x; cout << sum << '\n'; } } return 0; } ``` ### 代码解释 1. **初始化**: - `arr` 存储元素初始值,`last` 记录每个元素最后修改时间戳(初始为0) - `global_val` 和 `last_global` 分别存储全局修改的值和时间戳(初始为-1) - `sum` 初始化为数组元素总和 2. **操作处理**: - **单点修改(类型1)**: - 检查元素当前值:若单独修改时间晚于全局修改(`last[i] > last_global`),则取 `arr[i]`;否则取全局值 `global_val` - 更新 `sum = sum - old_val + x` - 更新 `arr[i] = x` 和 `last[i] = 当前操作号` - **全局修改(类型2)**: - 更新 `global_val = x` 和 `last_global = 当前操作号` - 直接计算 `sum = n * x` 3. **时间复杂度**: - 每次操作 $O(1)$,总复杂度 $O(n + q)$,满足题目要求 ### 示例测试 **输入**: ``` 5 5 1 2 3 4 5 1 1 5 2 10 1 5 20 2 1 1 3 100 ``` **输出**: ``` 19 // (5+2+3+4+5) 50 // 5*10 70 // 50 -10 +20 5 // 5*1 103 // 5 -1 +100 = 104? 注意:第三个元素旧值为1,改为100后:5-1+100=104 ``` 修正:最后一步详细计算: - 全局修改后所有元素为1(`sum=5`) - 修改元素3:旧值=1,新值=100 - `sum = 5 - 1 + 100 = 104`(代码中输出104)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值