传送门:http://codeforces.com/contest/1087/problem/F
题意:一排有n个人玩石头剪刀布,每个人会出一个固定的手势,游戏会进行n-1轮,每次会随机挑选两个相邻的人比对,输的人退出,如果平局则随机一个人退出,问有多少人有可能获得最终冠军,还有q个修改操作,每次询问修改后可能获得冠军的人数。
题解:假如一个人能获得冠军,那么他要都能赢左右两边的人,如果能打败一边的人,那么这一边肯定没有克制他的或者有克制他的也有他克制的,这样的话每次就是对每种手势的人做区间询问,用树状数组即可。
#include<bits/stdc++.h>
using namespace std;
char s[200005];
int n,mp[205],num[200005];
set<int>sp[3];
struct BIT{
int s[200005];
void add(int pos,int x){
while(pos<=n){
s[pos]+=x;
pos+=pos&-pos;
}
}
int query(int pos){
int ans=0;
while(pos){
ans+=s[pos];
pos-=pos&-pos;
}
return ans;
}
}b[3];
int ask(){
int ans=0;
for(int i=0;i<3;i++){
if(sp[(i+1)%3].empty()){
ans+=sp[i].size();
continue;
}
if(sp[(i+2)%3].empty())continue;
int l=(*sp[(i+2)%3].begin()),r=(*sp[(i+2)%3].rbegin());
ans+=b[i].query(r)-b[i].query(l-1);
int x=(*sp[(i+1)%3].begin()),y=(*sp[(i+1)%3].rbegin());
ans+=b[i].query(n)-b[i].query(max(y,r))+b[i].query(min(x,l)-1);
}
return ans;
}
int main(){
int q,i,j;
mp['R']=0;
mp['P']=1;
mp['S']=2;
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(i=1;i<=n;i++){
num[i]=mp[s[i]];
b[num[i]].add(i,1);
sp[num[i]].insert(i);
}
printf("%d\n",ask());
for(int i=1;i<=q;i++){
char c[3];
int p;
scanf("%d%s",&p,c);
b[num[p]].add(p,-1);
sp[num[p]].erase(p);
num[p]=mp[c[0]];
b[num[p]].add(p,1);
sp[num[p]].insert(p);
printf("%d\n",ask());
}
return 0;
}