CF - 496D Tennis Game -

problem:

http://codeforces.com/problemset/problem/496/D
一场比赛分若干set. 一个set分若干serve。两个人玩,每个serve其中一个人赢。
当一个人赢了t个serve,他就赢了一个set,然后两个人的serve score都清零。
当一个人赢了s个set,这场比赛他就赢了,比赛结束。
现在已知某一场比赛一共有n个serve和每个serve是谁赢的(1 or 2)。问这场比赛可能的s 和 t对。1<=n<=1e5

think:

最后一个serve赢的那个人一定是这场比赛的赢的人。
1.枚举t。t确定了,比赛的全部过程就全都确定了,能否可行以及s就都知道了。
2.已知t的话,找下一个set的时候用二分,不然O(n)的走会超时。已知第i个set的结束位置fr,找第i+1个set的结束位置to,那么就找最左边的to,使得[fr+1,to]有一个人赢了t个serve。如果最后那个set结束位置不是n肯定不行。
3.现在已知t,和整场比赛两个人分别赢的set的次数。看是否赢set最多的人和a[n]是同一个人。是的话这个t,s对是ok的。否则是不ok的。

code:

<pre name="code" class="cpp">const int N = 100100;

int a[N];
int one[N];//前i个有几个1
int two[N];//前i个有几个2
struct point{//最后的答案,最后排序后输出
    int x, y;
}p[N];
int n, ans;

bool cmp(point a, point b){
    if(a.y == b.y) return a.x < b.x;
    return a.y < b.y;
}

void upd(int i, int id, int xx, int yy, int y0){
    if(i == n && a[n] == id && yy > y0){
        //最后赢的人a[n],一定是结果赢的人。结果赢的人,一定是a[n]
        p[ans].x = xx;
        p[ans++].y = yy;
    }
}

int mid_find(int t, int fr, int to){//二分
    if(one[to] - one[fr] < t && two[to] - two[fr] < t) return to + 1;
    int L = fr + 1, R = to;
    while(L < R){
        int M = (L + R) >> 1;
        int t1 = one[M] - one[fr];
        int t2 = two[M] - two[fr];
        if(t1 < t && t2 < t) L = M + 1;
        else if(t1 > t || t2 > t) R = M - 1;
        else if(t1 == t && t2 == t) R = M - 1;
        else if(t1 == t) {
            if(a[M] == 1) return M;
            else R = M - 1;
        }
        else{
            if(a[M] == 2) return M;
            else R = M - 1;
        }
    }
    return L;
}

void check(int t){
    int num1 = 0, num2 = 0;
    int win1 = 0, win2 = 0;
    for(int i = 0; i < n; ){
        int ii = mid_find(t, i, n);//第i+1到第ii个是一场比赛,赢的人赢了t局
        if(ii > n) return ;
        num1 = one[ii] - one[i];
        num2 = two[ii] - two[i];
        if(num1 == t) {
            ++win1;
            upd(ii, 1, t, win1, win2);
        }
        else if(num2 == t){
            ++win2;
            upd(ii, 2, t, win2, win1);
        }
        i = ii;
    }
}

int main(){
    while(scanf("%d", &n) != EOF){
        one[0] = 0;
        two[0] = 0;
        int m = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            if(a[i] == 1) {
                ++m;
                one[i] = one[i - 1] + 1;
                two[i] = two[i - 1];
            } else {
                one[i] = one[i - 1];
                two[i] = two[i - 1] + 1;
            }
        }
        if(a[n] == 2) m = n - m;
        ans = 0;
        for(int i = 1; i <= m; ++i){
            check(i);
        }
        printf("%d\n", ans);
        sort(p, p + ans, cmp);
        for(int i = 0; i < ans; ++i) printf("%d %d\n", p[i].y, p[i].x);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值