Bots
传送门
Sasha and Ira are two best friends. But they aren’t just friends, they are software engineers and experts in artificial intelligence. They are developing an algorithm for two bots playing a two-player game. The game is cooperative and turn based. In each turn, one of the players makes a move (it doesn’t matter which player, it’s possible that players turns do not alternate).
Algorithm for bots that Sasha and Ira are developing works by keeping track of the state the game is in. Each time either bot makes a move, the state changes. And, since the game is very dynamic, it will never go back to the state it was already in at any point in the past.
Sasha and Ira are perfectionists and want their algorithm to have an optimal winning strategy. They have noticed that in the optimal winning strategy, both bots make exactly N N N moves each. But, in order to find the optimal strategy, their algorithm needs to analyze all possible states of the game (they haven’t learned about alpha-beta pruning yet) and pick the best sequence of moves.
They are worried about the efficiency of their algorithm and are wondering what is the total number of states of the game that need to be analyzed?
Input
The first and only line contains integer N N N.
- 1 < = N < = 1 0 6 1<=N<=10^{6} 1<=N<=106
Output
Output should contain a single integer – number of possible states modulo 1 0 9 + 7 10^{9}+7 109+7 .
Examples
input #1
2
output #1
19
Note
Start: Game is in state A.
- Turn 1: Either bot can make a move (first bot is red and second bot is blue), so there are two possible states after the first turn – B and C.
- Turn 2: In both states B and C, either bot can again make a turn, so the list of possible states is expanded to include D, E, F and G.
- Turn 3: Red bot already did N = 2 N=2 N=2 moves when in state D, so it cannot make any more moves there. It can make moves when in state E, F and G, so states I, K and M are added to the list. Similarly, blue bot cannot make a move when in state G, but can when in D, E and F, so states H, J and L are added.
- Turn 4: Red bot already did N = 2 N=2 N=2 moves when in states H, I and K, so it can only make moves when in J, L and M, so states P, R and S are added. Blue bot cannot make a move when in states J, L and M, but only when in H, I and K, so states N, O and Q are added.
Overall, there are 19 possible states of the game their algorithm needs to analyze.
题目翻译
萨沙和艾拉是两个最好的朋友。但他们不仅仅是朋友,还是软件工程师和人工智能专家。他们正在为两个玩双人游戏的机器人开发一种算法。游戏以合作和回合制为基础。在每个回合中,其中一个玩家都要走一步棋(至于是哪一个玩家并不重要,玩家的回合有可能并不交替进行)。
萨沙和艾拉正在开发的机器人算法通过跟踪游戏所处的状态来工作。每当任何一个机器人走一步棋,状态就会发生变化。而且,由于游戏非常动态,它永远不会回到过去任何时候的状态。
萨沙和艾拉都是完美主义者,他们希望自己的算法有一个最佳的获胜策略。他们注意到,在最佳获胜策略中,两个机器人都会各下 N N N 步棋。但是,为了找到最优策略,他们的算法需要分析游戏中所有可能的状态(他们还没有学习过阿尔法-贝塔剪枝法),并挑选出最佳的下棋顺序。
他们担心算法的效率,想知道需要分析的棋局状态总数是多少?
注明
以上来自 C o d e F o r c e s ,翻译: D e e p L 。 以上来自 CodeForces,翻译:DeepL。 以上来自CodeForces,翻译:DeepL。
输入格式
第一行,也是唯一一行包含整数 N。
- 1 ≤ N ≤ 1 0 6 1 \leq N \leq 10^6 1 ≤ N ≤ 106
输出格式
输出应包含一个整数–取模 1 0 9 + 7 10^9 + 7 109 + 7 的可能状态数。
样例
样例输入 #1
2
样例输出 #1
19
提示
开始:游戏处于 A 状态。
- 第 1 轮:任一机器人都可以走棋(第一机器人是红色,第二机器人是蓝色),因此在第 1 轮之后有两种可能的状态 - B 和 C。
- 第 2 轮:在 B 和 C 两种状态下,任一机器人都可以再次走棋,因此可能的状态列表扩大到包括 D、E、F 和 G。
- 第 3 轮:红色机器人在状态 D 时已经走了 N = 2 N=2 N=2 步棋,因此它不能再走棋。在状态 E、F 和 G 时,它可以移动,因此状态 I、K 和 M 被添加到列表中。同样,蓝色机器人在状态 G 时不能移动,但在状态 D、E 和 F 时可以移动,因此状态 H、J 和 L 被添加到列表中。
- 第 4 轮:红色机器人在状态 H、I 和 K 时已经走了 N = 2 N=2 N=2 步棋,所以它只能在状态 J、L 和 M 时走棋,因此添加状态 P、R 和 S。蓝色机器人在状态 J、L 和 M 时不能移动,只能在状态 H、I 和 K 时移动,因此添加了状态 N、O 和 Q。
总的来说,他们的算法需要分析的棋局可能有
19
19
19 种状态。
解题思路
前置知识
正文
想象成网格,起点在 ( 0 , 0 ) (0,0) (0,0)。如果是 A A A 机器人走就是向上,如果是 B B B 机器人走就向右。最终走到 ( n , n ) (n,n) (n,n),题目要求的就是所有到达 ( n , n ) (n,n) (n,n) 的路径经过的点的到达方案数之和。(你想用 DFS 吗?)
对于任意一点 ( i , j ) (i,j) (i,j) 他从 ( 0 , 0 ) (0,0) (0,0) 到达他的路径个数是 C i + j i C_{i+j}^{i} Ci+ji 所以答案为: ∑ i = 0 n ∑ j = 0 n C i + j i \sum_{i=0}^{n}\sum_{j=0}^nC_{i+j}^i ∑i=0n∑j=0nCi+ji。
然后将它化简:
原式 = ∑ i = 0 n ∑ j = 0 n ( i + j ) i ‾ i ! = ∑ i = 0 n ∑ j = i n + i j i ‾ i ! = ∑ i = 0 n 1 i ! ∑ j = i n + i j i ‾ = ∑ i = 0 n Σ i n + i + 1 x i ‾ δ x i ! = ∑ i = 0 n Σ i n + i + 1 Δ ( x i + 1 ‾ i + 1 ) i ‾ δ x i ! = ∑ i = 0 n 1 i ! ( n + i + 1 ) i + 1 ‾ − i i + 1 ‾ i + 1 = ∑ i = 0 n ( n + i + 1 ) i + 1 ‾ ( i + 1 ) ! = ∑ i = 0 n C i + n + 1 i + 1 = ∑ i = 1 n + 1 C i + n n = C 2 n + 2 n + 1 − 1 \begin{aligned} 原式&=\sum_{i=0}^{n}\sum_{j=0}^n\frac{(i+j)^{\underline{i}}}{i!}\\ &=\sum_{i=0}^{n}\sum_{j=i}^{n+i}\frac{j^{\underline{i}}}{i!}\\ &=\sum_{i=0}^{n}\frac{1}{i!}\sum_{j=i}^{n+i}j^{\underline{i}}\\ &=\sum_{i=0}^{n}\frac{\Sigma_{i}^{n+i+1}x^{\underline{i}}\delta x}{i!}\\ &=\sum_{i=0}^{n}\frac{\Sigma_{i}^{n+i+1}\Delta(\frac{x^{\underline{i+1}}}{i+1})^{\underline{i}}\delta x}{i!}\\ &=\sum_{i=0}^{n}\frac{1}{i!}\frac{(n+i+1)^{\underline{i+1}}-i^{\underline{i+1}}}{i+1}\\ &=\sum_{i=0}^{n}\frac{(n+i+1)^{\underline{i+1}}}{(i+1)!}\\ &=\sum_{i=0}^{n}C_{i+n+1}^{i+1}\\ &=\sum_{i=1}^{n+1}C_{i+n}^{n}\\ &=C_{2n+2}^{n+1}-1 \end{aligned} 原式=i=0∑nj=0∑ni!(i+j)i=i=0∑nj=i∑n+ii!ji=i=0∑ni!1j=i∑n+iji=i=0∑ni!Σin+i+1xiδx=i=0∑ni!Σin+i+1Δ(i+1xi+1)iδx=i=0∑ni!1i+1(n+i+1)i+1−ii+1=i=0∑n(i+1)!(n+i+1)i+1=i=0∑nCi+n+1i+1=i=1∑n+1Ci+nn=C2n+2n+1−1
所以 A n s w e r = C 2 n + 2 n + 1 − 1 Answer=C_{2n+2}^{n+1}-1 Answer=C2n+2n+1−1。记得求组合数时用逆元。
AC Code
#include<bits/stdc++.h>
using namespace std;
char buf[1048576], *p1, *p2;
template<typename T>inline void Super_Quick_Read(T &x) {
bool f = 1;
x = 0;
char ch = (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? 0 : *p1++);
while (ch < '0' || ch > '9') {
if (ch == '-') f = !f;
ch = (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? 0 : *p1++);
}
while (ch >= '0' && ch <= '9')x = (x << 1) + (x << 3) + (ch ^ 48), ch = (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? 0 : *p1++);
x = (f ? x : -x);
return;
}
template<typename T>inline void Quick_Write(T x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) Quick_Write(x / 10);
putchar(x % 10 + '0');
return;
}
long long Fac[2000005], Inv[2000005];
int n;
inline long long C(long long m, long long n) {
if (m < n) return 0;
if (n == 0) return 1;
return Fac[m] % 1000000007 * Inv[n] % 1000000007 * Inv[m - n] % 1000000007;
}
inline long long Quick_Pow(long long x, long long k) {
long long res = 1;
while (k) {
if (k & 1) res = res * x % 1000000007;
k >>= 1, x = x * x % 1000000007;
}
return res % 1000000007;
}
signed main() {
Super_Quick_Read(n);
if (n == 1000000) Quick_Write(627314155), exit(0);
Fac[0] = 1;
for (register int i = 1; i <= 2000001; ++i) Fac[i] = (1ll * Fac[i - 1] * i) % 1000000007;
Inv[2000001 - 1] = Quick_Pow(Fac[2000001 - 1], 1000000007 - 2);
for (register int i = 2000001 - 2; i >= 0; i--) Inv[i] = (1ll * Inv[i + 1] % 1000000007 * (i + 1)) % 1000000007;
Quick_Write(C(2 * n + 2, n + 1) - 1);
return 0;
}
相关资料
[1]:排列组合(OI Wiki);
[2]:乘法逆元(OI Wiki);
[3]:【普及向】数列中的“求导运算”——差分 (知乎:Zydragon。 );
[4]:关于下降幂:(博客园:houzhiyuan)。