Codeforces Round #528 (Div. 1 D. Rock-Paper-Scissors Champion 树状数组+思维

链接:http://codeforces.com/contest/1086/problem/D

 

题意:

     给你一个n长度的SRP组成的字符串,分别代表石头剪刀布,每次随机选取相邻的两个人进行对决,失败的一方退出队伍,相同则随机取一人退出,n-1次操作后剩下的那个人胜利。现在有q个询问,要你输出q+1个数字,每次询问会给你一个修改pos C,代表pos位置上的这个要修改成出C这个字母,输出的数字代表这样的情况下有几个人有可能会胜出,注意,修改是动态的,即字符串会随着这个人的修改而改变。

 

      说实话一开始是真的想不太到做法,看到是cf中2700+的题目就有点怕,没怎么过多想就看了题解,然后发现这道题其实挺简单的...只是自己吓自己...咳咳 

做法:

       我们这里先讨论最复杂的,也就是石头剪刀布都出现了的情况,我们可以统计石头剪刀布胜利的人分别有多少,假设我们现在要统计石头胜的人有多少,那么,我们先找出第一个剪刀的位置l和最后一个剪刀的位置r,对于i位置上的石头,如果l<i<r,那么这个i就有可能获胜,为什么呢,因为假设有布在石头周边,那么它一定有机会会和i左边和右边的剪刀碰面,即最后和石头碰上的可能是两边的剪刀,那么石头就会获胜,如果是石头的话因为随机胜利所以这个i石头也可能会胜利。如果还不能明白的话,那么我们就假设我们已经对字符串做了处理,相同的一长段都并在一起,那么我们得到的关于第i个石头附近的字符串会是 ...SRS..或者...SRPS..等,那么这个是有可能先和S一起,然后被这个石头处理掉的。如果i<l,那么我们就要看看这个i左边是否存在布,为什么呢,因为如果存在的话,缩减的字符串就会是..PRS...这种类型的,很明显(手模一下都知道),这种情况下的石头是不可能胜利的,右边也以此类推。

      所以对于最复杂的情况,我们有了分析,只有两种或者是一种的,都可以直接判断就好。

      这样的数据结构的利用就很神奇了,还是不容易想到啊,不愧是2700+的题。

 


#include<bits/stdc++.h>
using namespace std;
const int maxn=200050;
//0->剪刀 S,1->石头 R,2->布 P
int sum[3][maxn+10],po,q,aim[maxn];
set<int> pos[3];
set<int>::iterator it;
int n,aga[3][2]={{2,1},{0,2},{1,0}};
//aga中 0为小于它 1为大于它
char s[maxn],tmp[5];
int lowbit(int x){
    return x&(-x);
}
int query(int x,int sum[]){
    int ans=0;
    while(x){
        ans+=sum[x];
        x-=lowbit(x);
    }
    return ans;
}
void add(int x,int num,int sum[]){
    while(x<maxn){
        sum[x]+=num;
        x+=lowbit(x);
    }
}
int Cal(int now){
    if(pos[now].size()==0) return 0;
    int ruo=aga[now][0],qiang=aga[now][1],aans=0;
    if(pos[ruo].size()==0){
        if(pos[qiang].size()!=0) return 0;
        else return pos[now].size();
    }
    else{
        if(pos[qiang].size()==0) return pos[now].size();
        int l=*pos[ruo].begin(),r=*pos[ruo].rbegin();
        aans+=(query(r,sum[now])-query(l,sum[now]));
        int st=*pos[qiang].begin(),en=*pos[qiang].rbegin();
        int qian=min(st,l),hou=max(r,en);
        aans+=query(qian,sum[now])+query(maxn,sum[now])-query(hou,sum[now]);
    }
    return aans;
}
int gain(char a){
    if(a=='S')  return 0;
    else if(a=='R')  return 1;
    else return 2;
}
int main(){
    scanf("%d%d%s",&n,&q,s+1);
    for(int i=1;i<=n;i++){
        aim[i]=gain(s[i]);
        add(i,1,sum[aim[i]]);
        pos[aim[i]].insert(i);
    }
    printf("%d\n",Cal(0)+Cal(1)+Cal(2));
    for(int i=1;i<=q;i++){
        scanf("%d%s",&po,tmp+1);
        int obj=gain(tmp[1]),yuan=aim[po];
        add(po,-1,sum[yuan]); pos[yuan].erase(po);
        add(po,1,sum[obj]); pos[obj].insert(po);
        printf("%d\n",Cal(0)+Cal(1)+Cal(2));
        aim[po]=obj;
    }
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值