CF-Codeforces Round #706 (Div. 2)-1496D. Let‘s Go Hiking【分类讨论/思维】

题目链接
题意:一个大小为 n n n的数组 p p p ( p p p 1 , 2 , 3 , . . . , n {1,2,3,...,n} 1,2,3,...,n的一个排列),现在 A , B A,B A,B进行这样一个游戏, A A A先选定一个初始位置 x x x B B B再选定一个初始位置 y y y(选定的位置是公开的),然后开始移动,移动规则如下:
A A A: 选定 x x x A A A移动到的位置,且需满足 x ≠ y , 1 ≤ x ′ ≤ n , ∣ x − x ′ ∣ = 1 , p [ x ′ ] < p [ x ] x≠y,1 \le x'\le n,|x-x'|=1,p[x']<p[x] x=y,1xn,xx=1p[x]<p[x]
B B B: 选定 y y y B B B移动到的位置,且需满足 y ≠ x , 1 ≤ y ′ ≤ n , ∣ y − y ′ ∣ = 1 , p [ y ′ ] > p [ y ] y≠x,1\le y'\le n,|y-y'|=1,p[y']>p[y] y=x,1yn,yy=1p[y]>p[y]
当一方不能移动时,输掉游戏。问先手的胜负。
思路:为了方便叙述,我把这个问题看作一个在山峰上移动的过程,A只能下坡,B只能上坡,记 l l l为最长单调序列的长度(最长坡的长度), c c c为最长单调序列的数量。并且约定奇数轮A移动,偶数轮B移动。
显然A的最优选择是从山峰开始,B的最优选择是从最底下开始,我们不难发现:

  1. 当最长单调序列数量大于2时, c > 2 c>2 c>2,A选择某一个最高山峰(某最长单调序列的最大值)后,B可以选另一个最高山峰对应的谷底(另一最长单调序列的最小值),这样经过 c c c回合后,A就无法继续移动了,即先手负
  2. 当最长单调序列数量等于1时, c = 1 c=1 c=1,A选择一个最高山峰,B都可以控制AB间距离为偶数,这样就使得 ∣ A B ∣ / 2 |AB|/2 AB/2轮后,A无法选择。 例如序列 1 , 2 , 6 , 5 , 4 , 3 {1,2,6,5,4,3} 1,2,6,5,4,3,A选择7,那么B直接选择4,这样如果A选择向左侧移动,那显然3回合后就无法移动;如果A选择向右侧移动,那四轮以后A站在6的位置上,B站在5的位置上,第五轮到A时,A无法移动,先手负
  3. 当最长单调序列数量等于2时, c = 2 c=2 c=2,只有两个单调序列类似于 p m − l + 1 < p m − l + 2 < ⋯ < p m > p m + 1 > ⋯ > p m + l − 1 p_{m-l+1}<p_{m-l+2}<\cdots<p_m>p_{m+1}>\cdots>p_{m+l-1} pml+1<pml+2<<pm>pm+1>>pm+l1,A才有可能赢(否则可以参考 c > 2 c>2 c>2的情况让先手负),那么需要考虑最长单调序列长度的奇偶性,如果 c % 2 = 0 c\%2=0 c%2=0,那么无论是A与B同向而行还是相向而行都会在偶数轮让A无路可走,即 先手负如果 c % 2 = 1 c\%2=1 c%2=1,那么A显然第一轮选择山峰,如果随后B选择了最低点,那A就控制自己与B相向而行,由于长度是奇数,最后B会无法选择,如果B不选择最低点而是控制AB距离为偶数,那A随后就可以选择和B同向移动,这样A能移动的长度会比B长,所以这种情况下先手胜

总结以上情况,认真分类讨论,不难得出结论。
AC代码:

#include <bits/stdc++.h>
#define pcc pair<char, char>
#define pii pair<int, int>
#define vi vector<int>
#define vl vector<ll>
#define rep(i, x, y) for (int i = x; i < y; i++)
#define per(i, x, y) for (int i = x; i >= y; i--)
#define rep0(i, n) for (int i = 0; i < (n); i++)
#define per0(i, n) for (int i = (n)-1; i >= 0; i--)
#define mp make_pair
#define pb push_back
#define F first
#define S second
#define sz(x) (x).size()
#define all(x) (x).begin(), (x).end()
#define ll long long
#define ull unsigned long long
#define db double
#define ld long double
using namespace std;

inline int read() {
  int x = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + ch - '0';
    ch = getchar();
  }
  return x * f;
}
const double eps = 1e-9;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 10;
const int MAX = 1e5;
const ll INF = 0x3f3f3f3f3f3f3f;
/***************main****************/
ll T = 1;
ll m, n;
ll a[maxn], l[maxn], r[maxn], mi[maxn];
int main() {
  n = read();
  ll cnt1 = 0, cnt2 = 0;
  rep(i, 0, n) {
    a[i] = read();
    l[i] = r[i] = 1;
  }
  ll ma = 0;
  rep(i, 1, n) if (a[i] > a[i - 1]) l[i] = l[i - 1] + 1;
  per(i, n - 2, 0) if (a[i] > a[i + 1]) r[i] = r[i + 1] + 1;
  rep(i, 0, n) ma = max(max(l[i], r[i]), ma);
  rep(i, 0, n) if (l[i] == ma || r[i] == ma) {
    if (l[i] == r[i]) cnt1++;
    cnt2++;
  }
  if (cnt1 + cnt2 != 2)
    cout << 0 << endl;
  else if (cnt1 != 1)
    cout << 0 << endl;
  else if (ma & 1)
    cout << 1 << endl;
  else
    cout << 0 << endl;
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值