Codeforces Round#528 div2 F. Rock-Paper-Scissors Champion

文章来自  Icontofig 's Blog // 我们的征程是星辰大海!Away From OI,Come to ICPC(查看友链请点About Me)

Codeforces Round#528 div2

F. Rock-Paper-Scissors Champion

time limit:per test 2 seconds
memory limit:per test 256 megabytes
input:standard input
output:standard output

n players are going to play a rock-paper-scissors tournament. As you probably know, in a one-on-one match of rock-paper-scissors, two players choose their shapes independently. The outcome is then determined depending on the chosen shapes: "paper" beats "rock", "rock" beats "scissors", "scissors" beat "paper", and two equal shapes result in a draw.

At the start of the tournament all players will stand in a row, with their numbers increasing from 1 for the leftmost player, to n for the rightmost player. Each player has a pre-chosen shape that they will use in every game throughout the tournament. Here's how the tournament is conducted:

1.If there is only one player left, he is declared the champion.
2.Otherwise, two adjacent players in the row are chosen arbitrarily, and they play the next match. The losing player is eliminated from the tournament and leaves his place in the row (with his former neighbours becoming adjacent). If the game is a draw, the losing player is determined by a coin toss.
The organizers are informed about all players' favoured shapes. They wish to find out the total number of players who have a chance of becoming the tournament champion (that is, there is a suitable way to choose the order of the games and manipulate the coin tosses). However, some players are still optimizing their strategy, and can inform the organizers about their new shapes. Can you find the number of possible champions after each such request?

Input

The first line contains two integers n and q — the number of players and requests respectively (1≤n≤2⋅10^5, 0≤q≤2⋅10^5).

The second line contains a string of n characters. The i-th of these characters is "R", "P", or "S" if the player i was going to play "rock", "paper", or "scissors" before all requests respectively.

The following q lines describe the requests. The j-th of these lines contain an integer pj and a character cj meaning that the player pj is going to use the shape described by the character cj from this moment (1≤pj≤n).

Output

Print q+1 integers r0,…,rq, where rk is the number of possible champions after processing k requests.

Example

input
3 5
RPS
1 S
2 R
3 P
1 P
2 P
output
2
2
1
2
2
3

题解


大体题意:

这是一个剪刀石头布的游戏,每个人每轮要出的拳已经决定好了,每轮每次比拼随机抽取两位相邻的人比赛,输的人被淘汰,如果两人平局,随机淘汰一人。而且,每轮将会有一个人改变他的出拳方式。求问对于每一轮有多少人可能成为最后的胜者。

真正题解:

此题是个线段树!!!!!(当然树状数组也行)
这道题的线段树掩藏的是很深的。我们不难看出来。这题其实就是一个有关点操作的题目。修改出拳方式是一种点修改的操作,确实是可以使用线段树和树状数组来实现的,但是哪里有区间操作?或者说,这个题目哪里有和区间求和有关的地方?
先不着急,我们先来看看答案该怎么算。求每一轮有多少人可能成为最后的胜者,我们当然也就可以来求,每一轮必定有多人会被淘汰。这两个问题是近似等价的。
那么到底什么样的人是一定会被淘汰的呢?这点才是这个题目的核心,理解了这个核心以后,这道题的数学模型就非常简单了。
那么哪些人是一定会被淘汰的呢?
首先我们来看最简单的情况,就是所有人都出一样的拳,那么所有人都有可能成为最终的胜利者,不存在必定被淘汰的人。
第二种情况,所有人出的拳总共只有两种,那么必定被淘汰的就是出必输的那一种拳的所有人。这种情况,我们就直接求出n个人中所有出必输的那种拳的人的个数就是必定被淘汰的人的个数了,这种情况我们可以通过对区间1——n中进行区间求和完成。
第三种,也是最复杂的情况。三种拳都出现了。那我们考虑一下,对于出每一种拳的人,满足什么条件的人必定被淘汰呢?结论是:如果这个人的被夹在第一个出胜他的拳的人和第一个出负于他的拳的人之间,或者被夹在最后一个出负于他的拳的人和最后一个出胜他的拳的人之间(注意这里胜负手的顺序!这很关键),那么这个人一定会被淘汰。
先来说说正确性。这两种夹是有对称性的,所以我们只考虑开头一侧好了。如果这个人被后面的胜出而来人淘汰了,那他就是被淘汰了。如果他没有被后面胜出的人淘汰,那他也一定会被他前面那个胜他的人淘汰,所以这样的人是一定会被淘汰的。
再来说说唯一性。如果不是这两种情况呢?那么他即便被夹在胜他的人之间,他两侧的胜他的人也有可能被之前和之后的人淘汰,而他却不一定被胜出而来挑战他的人击败,从而他有可能成为最终的胜者。
那么第三种情况非常清晰的,就是一个区间求和。
现在我们要做的,就是区间求和与点修改,那么显然就是树状数组或者线段树了!


题解代码(线段树)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
set<int>x[3];
vector<int>v;
int n,q;
const int maxn = 2e5+5;
struct seg{
    int l,r,val[3];
}tr[maxn<<2];
void push_up(int now){
    for(int i = 0;i < 3;++i){
        tr[now].val[i] = tr[now<<1].val[i] + tr[now<<1|1].val[i];
    }
    return;
}
void build(int now,int l,int r){
    tr[now].l = l;
    tr[now].r = r;
    for(int i = 0;i < 3;++i)
        tr[now].val[i] = 0;
    if(l == r){
        tr[now].val[v[l]] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    push_up(now);
    return;
}
void update(int now,int pos,int vc){
    if(tr[now].l == tr[now].r && tr[now].l == pos){
        for(int i = 0;i < 3;++i)
            tr[now].val[i] = 0;
        tr[now].val[vc] = 1;
        return;
    }
    int mid = (tr[now].l + tr[now].r) >> 1;
    if(pos <= mid)update(now<<1,pos,vc);
    else update(now<<1|1,pos,vc);
    push_up(now);
    return;
}
int query(int now,int ql,int qr,int vc){
    if(qr < ql)return 0;
    if(tr[now].l >= ql && tr[now].r <= qr){
        return tr[now].val[vc];
    }
    int mid = (tr[now].l + tr[now].r) >> 1;
    int ans = 0;
    if(ql <= mid)ans += query(now<<1,ql,qr,vc);
    if(qr > mid)ans += query(now<<1|1,ql,qr,vc);
    return ans;
}
int get_ans(){
    int ans = n;
    for(int i = 0;i < 3;++i){
        int j = (i + 1) % 3;
        int k = (j + 1) % 3;
        if(!x[j].empty()){
            if(!x[k].empty()){
                ans -= query(1,*x[j].begin(),*x[k].begin(),i);
                ans -= query(1,*(--x[k].end()),*(--x[j].end()),i);
            }
            else ans -= query(1,0,n-1,i);
        }
    }
    return ans;
}
int change(char c){
    int ret;
    switch(c){
        case 'R' : ret = 0;break;
        case 'P' : ret = 1;break;
        case 'S' : ret = 2;break;
        default : break;
    }
    return ret;
}
string s;
int a;
char ch;
int main(){
    cin >> n >> q;
    cin >> s;
    for(int i = 0;i < n;++i){
        ch = s[i];
        v.push_back(change(ch));
        x[change(ch)].insert(i);
    }
    build(1,0,n-1);
    cout << get_ans() << endl;
    while(q--){
        cin >> a >> ch;
        a--;
        x[v[a]].erase(x[v[a]].find(a));
        v[a] = change(ch);
        x[change(ch)].insert(a);
        update(1,a,change(ch));
        cout << get_ans() << endl;
    }
    return 0;
}​
Sign in to leave a comment.
No Leanote account ? Sign up now.
0 comments
Title - Artist
0:00