Codeforces Round #378 (Div. 2) E. Sleep in Class (思考,双指针,模拟)

7 篇文章 0 订阅

题意:有一段楼梯长度为n(1<=n<=1e6),每一阶楼梯上有一个字母,字母要么是‘U’要么是‘D’,楼梯上还有一个人,若他所处台阶是‘U’,则他会走上上一阶台阶,并继续根据上面的字母走路;若是‘D ’,则向下走,他每经过一个台阶,台阶上的字母就会改变(原先是‘U’就变成‘D’,‘D’变‘U’,这样……)。当这个人掉出楼梯时,计步结束,请给出这个人以每一阶台阶为出发点时,分别要走几步才会掉出楼梯。(如果不会掉出楼梯就输出-1)

思路:说实话这题的确很考验人的思维……
然后自己也参考了别人的代码(QAQ): http://blog.csdn.net/fsss_7/article/details/53170360
首先有这样一个结论,不管楼梯上字母分布如何,这个人一定会掉出楼梯,具体证明可以看 题解,大致证明思路就是假如这个人在往上走,他要么一只往上走,直到掉出,要么遇到一个‘D’,然后开始一直往下走,同样,他要么一直往下走直到掉出,要么遇到一个‘U’,重新开始往上走,此时之前使他往下走的那个‘D’已经变成了‘U’,因为阶梯数是有限的,所以他一定会走出楼梯。

1.先讨论从楼梯的哪个方向掉下
明白了上述结论后,很容易想到,楼梯上可以改变人行走方向的点只有出发点前面的‘U’,和出发点后面的‘D’。进一步简化问题,设当前点为x,因为x前面的点起作用的都是‘U’点,所以考虑x前面的点都是‘U’,后面的点都是‘D’的特殊情况。
比如:UUxDD,此时x前后的U,D数量相等,他们对人的行走方向的影响互相抵消,容易想到这种情况下掉出去的方向取决于x的方向。(x为D,从前掉,x为U,从后掉)
从上面的讨论展开:设x前U的数量为cntu,若从x(包括x)开始往后有cntd>cntu,则一定是从楼梯的前方掉下去的;若从x(包括x)开始往后有cntd<=cntu那么就是从楼梯的后方掉下去的。

2.计数方法
从上面的关于方向的讨论可以看出,人走路的模式大致是:在一个个U,R之间来回走,直到把这些障碍都清除了,然后再笔直地走到楼梯外,设x点走到它前面的每一个U点的 来回距离之和为X,x点走到它后面的每一个D点的 来回距离之和为Y,则我们一旦确定了跳下去的方向,答案就清楚了。
     向前跳:ans=X+Y+x
     向后跳:ans=X+Y+n-x+1
于是程序要做的事情就是:从头开始遍历每一个台阶,用一个双指针来维护当前的cntu和cntd个数,并不断更新X,Y。
关于更新的细节:
由于是从前往后扫(当然你愿意也可以从后往前扫QAQ),所以对于每个台阶,前面的cntu是一直的,所以就要用双指针找后面的d,尽量使cntd>cntu,如果找不到,那么说明人是往后跳的,要减去一些前面多余的U

详见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=1e6+10;
int n;
char str[MAXN];

int main(){
    while(~scanf("%d%s",&n,str+1)){
        LL u=0,d=0,x=0,y=0,l=0,r=0;
        for(int i=1;i<=n;++i){
            if(str[i-1]=='U') ++u;
            if(str[i-1]=='D') --d;
            x+=2*u;
            y-=2*d;
            while(u>=d&&r<n) if(str[++r]=='D') ++d,y+=2*(r-i);
            while(u>d&&r==n) if(str[++l]=='U') --u,x-=2*(i-l);
            if(u==d) printf("%I64d ",x+y+1+n-i);
            else printf("%I64d ",x+y+i);
        }
        putchar(10);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值