题目 | Putting Candies
AtCoder Beginner Contest 241(Sponsored by Panasonic)
E - Putting Candies
https://atcoder.jp/contests/abc241/tasks/abc241_e
Time Limit: 2 sec / Memory Limit: 1024 MB
Score : $500$ points
Problem Statement
You are given a sequence $A=(A_0,A_1,\ldots,A_{N-1})$of length $N$.
There is an initially empty dish. Takahashi is going to repeat the following operation $K$ times.
- Let $X$ be the number of candies on the dish. He puts $A_{(X\bmod N)}$ more candies on the dish. Here, $X\bmod N$ denotes the remainder when $X$ is divided by $N$.
Find how many candies are on the dish after the $K$ operations.
Constraints
- $2 \leq N \leq 2\times 10^5$
- $1 \leq K \leq 10^{12}$
- $1 \leq A_i\leq 10^6$
- All values in input are integers.
Input
Input is given from Standard Input in the following format:
$N$ $K$
$A_0$ $A_1$ $\ldots$ $A_{N-1}$
Output
Print the answer.
Sample Input 1
5 3
2 1 6 3 1
Sample Output 1
11
The number of candies on the dish transitions as follows.
- In the $1$-st operation, we have $X=0$, so $A_{(0\mod 5)}=A_0=2$ more candies will be put on the dish.
- In the $2$-nd operation, we have $X=2$, so $A_{(2\mod 5)}=A_2=6$ more candies will be put on the dish.
- In the $3$-rd operation, we have $X=8$, so $A_{(8\mod 5)}=A_3=3 $more candies will be put on the dish.
Thus, after the $3$ operations, there will be $11$ candies on the dish. Note that you must not print the remainder divided by $N$.
Sample Input 2
10 1000000000000
260522 914575 436426 979445 648772 690081 933447 190629 703497 47202
Sample Output 2
826617499998784056
The answer may not fit into a $32$-bit integer type.
我的笔记
发现循环
$X\bmod N$ 的结果只可能在 $0\sim N-1$ 中,因此至少 $N$ 次运算内会出现两次一样的增量,出现一样的增量就代表着之后会开始循环。
以 Sample Input 1 为例,每次的增量为 $2、6、3、1、6、3、1\dots$ 可见出现 $6、3、1$ 的循环。
所以我们可以计算出循环,再算出循环的次数,直接将一次循环的增量乘循环次数即可,省去大量时间。
计算循环
当两次 $X\bmod N$ 结果相同的时候,就说明出现了第一次循环。因此用一个数组 pre[ ],先初始化为 $-1$,然后用来储存上一次出现 $X\bmod N$ 的步数。如果某一步开始发现 pre[$X\bmod N$] 不为 $-1$,就说明开始循环了,那么从 pre[$X\bmod N$] 到该步的上一步就是一次循环的结果。
计算结果
rep_beg (begin)代表循环开始处,rep_end 代表循环结束处,rep_sum 代表一次循环的增量,rep_feq (frequence)代表一次循环的周期,rep_tim (time)代表循环次数,rep_rmn (remain)代表剩余的部分。
第一次 pre[candy[i + 1] % N] != -1
时:
rep_beg = pre[candy[i + 1] % N]
rep_end = i + 1
因此:
rep_frq = rep_end - rep_beg
rep_tim = (K - rep_beg) / rep_frq;
rep_rmn = (K - rep_beg) % rep_frq;
最后结果 ans = candy[rep_beg] + rep_sum * rep_tim + (candy[rep_beg + rep_rmn] - candy[rep_beg])
即从 0 ~ rep_beg 不属于循环的部分 + 循环一次的增量 * 循环次数 + 最后凑不到一次循环的剩余量,然后化简即可得到代码中的写法。
代码
#include <bits/stdc++.h>
#define SIZE 200010
using namespace std;
long long A[SIZE], candy[SIZE], pre[SIZE];
int main(void)
{
long long N, K;
cin >> N >> K;
for (int i = 0; i < N; i++)
cin >> A[i];
for (int i = 0; i < N; i++)
pre[i] = -1;
candy[0] = 0;
pre[0] = 0;
long long rep_beg = 0, rep_end = 0;
for (long long i = 0; i < N; i++)
{
candy[i + 1] = candy[i] + A[candy[i] % N];
if (pre[candy[i + 1] % N] != -1)
{
rep_beg = pre[candy[i + 1] % N];
rep_end = i + 1;
break;
}
pre[candy[i + 1] % N] = i + 1;
}
if (K <= rep_beg)
{
cout << candy[K] << endl;
}
else
{
long long rep_sum = candy[rep_end] - candy[rep_beg]; // 循环部分的和
long long rep_frq = rep_end - rep_beg; // 循环部分的周期
long long rep_tim = (K - rep_beg) / rep_frq; // 循环次数
long long rep_rmn = (K - rep_beg) % rep_frq; // 最后剩余的部分
long long ans = rep_sum * rep_tim + candy[rep_beg + rep_rmn]; // 结果
cout << ans << endl;
}
return 0;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi