814(Div.2)A~C
LZC Lv4

Codeforces Round #814 (Div. 2)

A. Chip Game

题目大意:

有一个n*m的棋盘,开局时棋子在左下角,两个玩家轮流移动,每次只能向上或向右移动奇数个格子,最后不能移动的玩家就输了。

思路:

由于每个人每次只能移动奇数个格子,我们可以直接移动最大奇数格子数,所以第一个玩家Burenka想赢的话,一定是在第三轮赢得比赛。

当n和m同为偶数时,最少2轮结束游戏,Tonya赢

当n和m同为奇数时,最少4轮结束游戏,Tonya赢

当n和m奇偶性不同时,最少3轮结束游戏,Burenka赢

AC Code

1
2
3
4
5
6
7
8
9
10
11
void solve()
{
int n,m;
cin >> n >> m;
string a = "Burenka";
string b = "Tonya";
if ((n % 2 == 0 && m % 2 == 0) || (n % 2 == 1 && m % 2 == 1))
cout << b << endl;
else cout << a << endl;

}

B. Mathematical Circus

题目大意:

给定两个数n,k,是否能用1~n所有的数组成n2\frac n 2个数对{a,b},满足(a+k)*b能被4整除。

如果满足条件则输出YES后输出全部结果,如果不满足条件则输出NO。

思路1:

要想满足条件,则一定要有n2\frac n 2个数对。

分类讨论:

  1. k为奇数时,(a+k) * b为偶数*偶数,则能被4整除。

  2. k为偶数时

    ​ 1). k能被4整除:只要a和b其中一个有一个奇数,就不能满足(a + k) * b能被4整除,所以输出NO

    ​ 2). k不能被4整数:则只要给每个奇数分配一个偶数就可以,b能被4整除时,a为奇数;a+k能被4整除时,b为奇数。

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void solve()
{
int n,k;
cin >> n >> k;
if (n == 2 && k == 0)
{
puts("NO");
return;
}

if (k % 4 == 0)
{
puts("NO");
return;
}

if (k & 1)
{
puts("YES");
for (int i = 1;i <= n;i += 2)
printf("%lld %lld\n",i,i + 1);
}
else
{
puts("YES");
for (int i = 2;i <= n;i += 4)
printf("%lld %lld\n",i,i - 1);
for (int i = 3;i <= n;i += 4)
printf("%lld %lld\n",i,i + 1);
}
}

思路2:

要想满足条件,则一定要有n2\frac n 2个数对。

直接枚举相邻数对{1,2}、{3,4}…{n - 1,n}。

对于每个数对{x,y},如果(x + k) * y能被4整除,则添加进答案,否则如果(y + k) * x能被4整除,则添加进答案,如果都不能则不添加。

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve()
{
int n,k;
cin >> n >> k;
vector<PII> ans;
for (int i = 1;i <= n;i += 2)
{
int a = i,b = i + 1;
int x = (a + k) * b,y = (b + k) * a;
if (x % 4 == 0) ans.push_back({a,b});
else if (y % 4 == 0) ans.push_back({b,a});
}

if (ans.size() == n / 2)
{
puts("YES");
for (auto it:ans)
cout << it.first << ' ' << it.second << '\n';
}
else
puts("NO");
}

C. Fighting Tournament

题目大意:

有n名运动员参加比赛,标号为1~n。第i名运动员的实力是a[i] (1 <= a[i] <= n)。每个运动员的实力都是不同的。

一开始,运动员按标号从小到大排成一列,第一位为1号运动员。

每一轮比赛,队头的两个人记性格斗,赢的人变成队头,输的人排到队尾。

q次询问,每次询问给出i和k,问i号运动员在前k次比赛中赢了几场。

思路:

当某个人的能量为n时,则无人能赢他,所以我们先找到能量最大的人,记为max。

所以出现在max后面的人,都没有赢的机会。

先初始化每个人第一次能赢的场次和最后一次能赢的场次。

对于每次询问,可以分类讨论:

选手i在max后面,则能赢的场次为0

  1. 如果k小于选手i第一次赢需要耗费的场次,则能赢的场次为0
  2. 如果选手i第一次赢需要耗费的场次为0,则说明他没赢过,最后赢的场次为0
  3. 如果k大于选手i最后一次赢得场次,则他能赢的场次为 最后一次赢-第一次赢 + 1
  4. 如果k小于选手i最后一次硬的场次,则他能赢的场次为 k - 第一次赢 +1
  5. 如果选手i为max,则他能赢的场次为 k - 第一次赢 + 1

AC Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void solve()
{
int n,q,k;
int maxx = -INF;
cin >> n >> q;

vector<int> a(n + 1);
vector<PII> res(n + 1);
for (int i = 1;i <= n;i ++)
{
cin >> a[i];
if (a[i] == n) maxx = i;
}
int mx = a[1],idx = 1,cnt = 1;
for (int i = 2;i <= n;i ++)
{
if (a[i] < mx)
{
if (res[idx].second == 0) res[idx].first = cnt;
res[idx].second = cnt;
}
else
{
mx = a[i];
idx = i;
res[idx].first = cnt;
res[idx].second = cnt;
}
cnt ++;
}

while(q --)
{
cin >> idx >> k;
int ans = 0;
bool can = idx > maxx || idx > k + 1 || res[idx].first > k || res[idx].first == 0;
if (!can && idx == maxx) ans = k - res[maxx].first + 1;
else if (!can)
{
if (k >= res[idx].second) ans = res[idx].second - res[idx].first + 1;
else ans = k - res[idx].first + 1;
}
cout << ans << endl;
}
}