CF1495B Lets Go Hiking

tag:博弈论


一个简单粗暴的思路,枚举每一个初始位置,然后判断可行性。

首先如果先手只能往一个方向走,那么后手直接堵旁边就赢了,所以只考虑先手两边都可以走的情况。

那么对于后手来说,他有两种选择:

  • 和先手比长度

  • 堵路

设先手能走到的范围为 \([l,r]\)

对于第一种情况,相当于二者互不干扰,则初始位置 \(i\in[1,l)\)\((r,n]\),或者从 \(l\) 往左走,从 \(r\) 往右走。

而堵路就两种情况,\(l\) 往右走和 \(r\) 往左走,显然后手会走长的那一条。

至于先手/后手往左/右能走多远,可以先扫一遍预处理 \(O(n)\)

for(register int i=1, lst=1; i<=n; i++)    
    if(a[i]>a[i+1] or i==n){
        for(register int j=lst; j<=i; j++) up[1][j] = i, down[0][j] = lst;
        lst = i+1;
    }
for(register int i=n, lst=n; i; i--)
    if(a[i]>a[i-1] or i==1){
        for(register int j=lst; j>=i; j--) up[0][j] = i, down[1][j] = lst;
        lst = i-1;
    }

后手在前缀/后缀中能走的最远长度也可以扫一遍预处理 \(O(n)\)

for(register int i=1; i<=n; i++) pre[i] = max(pre[i-1],max(i-up[0][i]+1,up[1][i]-i+1));
for(register int i=n; i>=1; i--) suf[i] = max(suf[i+1],max(i-up[0][i]+1,up[1][i]-i+1));

然后枚举一下先手的初始位置 \(O(1)\)判断,也是 \(O(n)\)

for(register int i=1; i<=n; i++) if(down[0][i]!=i and down[1][i]!=i){
    int len1 = i-down[0][i]+1, len2 = down[1][i]-i+1;
    int l = down[0][i], r = down[1][i];
    if(max(max(pre[l-1],l-up[0][l]+1),max(suf[r+1],up[1][r]-r+1))>=max(len1,len2)) continue;
    if(((len1>>1)<<1)>=len2) continue;
    if(((len2>>1)<<1)>=len1) continue;
    ans++;
}

注意一个小细节,如果要堵路,那么要保证两者间的距离为偶数,如果为奇数的话先手就能反杀。(模拟一下不难发现)

posted @ 2021-06-26 14:13  oisdoaiu  阅读(43)  评论(0编辑  收藏  举报