Codeforces 1629F. Game on Sum
Easy Version
\(\texttt{Difficulty:2100}\)
題目大意
\(\texttt{Alice}\) 和 \(\texttt{Bob}\) 進行一個遊戲,遊戲初始分數 \(x=0\) ,每輪 \(\texttt{Alice}\) 先指定一個實數 \(t\in[0,k](0\le k\le10^9+7)\) ,之後 \(\texttt{Bob}\) 可以選擇令 \(x=x+t\) 或是 \(x=x-t\) ,\(\texttt{Bob}\) 令 \(x=x+t\) 的次數至少為 \(m(1\le m\le2000)\) ,遊戲總共進行 \(n(1\le n\le2000)\) 輪,\(\texttt{Alice}\)
思路
考慮 \(dp\) ,設 \(f_{i,j}\) 為遊戲進行了 \(i\) 輪, \(\texttt{Bob}\) 執行了 \(j\) 次加法操作時, \(x\) 的值。
顯然 \(f_{i,j}\) 可以由 \(f_{i-1,j}\) 和 \(f_{i-1,j-1}\) 轉移而來,分別可以得到 \(f_{i-1,j}-t\), \(f_{i-1,j-1}+t\) 。顯然對於所有 \(t\) 的取值,兩個結果分別遞增和遞減, 而 \(\texttt{Bob}\)
程式碼
#include<bits/stdc++.h> #include<unordered_map> #include<unordered_set> using namespace std; using LL = long long; using ULL = unsigned long long; using PII = pair<int, int>; using TP = tuple<int, int, int>; #define all(x) x.begin(),x.end() #define mk make_pair //#define int LL //#define lc p*2 //#define rc p*2+1 #define endl '\n' #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #pragma warning(disable : 4996) #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) const double eps = 1e-8; const LL MOD = 1000000007; const LL mod = 998244353; const int maxn = 2010; LL T, N, M, K, f[maxn][maxn], two = 500000004; void solve() { for (int i = 1; i <= N; i++) f[i][i] = i * K % MOD; for (int i = 1; i <= N; i++) { for (int j = 1; j < i; j++) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % MOD * two % MOD; } cout << f[N][M] << endl; } int main() { IOS; cin >> T; while (T--) { cin >> N >> M >> K; for (int i = 1; i <= N; i++) { for (int j = 1; j <= M; j++) f[i][j] = 0; } solve(); } return 0; }
Hard Version
\(\texttt{Difficulty:2400}\)
資料範圍變為 \((1\le n,m\le10^6)\) 。
思路
可以發現在 \(\texttt{Easy Version}\) 進行的 \(dp\) 中,所有的答案都是由 \(f_{i,i}\) 推出,我們考慮對於 \(f_{n,m}\) ,每個 \(f_{i,i}\) 對於答案的貢獻是怎樣的,發現每個 \(f_{i,i}\) 的計算次數就是 \(f_{i+1,i}\) 的計算次數,而對於 \(f_{i,j}(i>j)\) ,其每次可以向 \(f_{i+1,j}\) 和 \(f_{i+1,j+1}\) 轉移,也就是向正下方和右下方轉移,其計算次數就是轉移到 \(f_{n,m}\) 的總路徑數,即 \(\binom{n-i}{m-j}\) ,顯然只有 \(i\le m\) 時的 \(f_{i,i}\) 才會對答案產生貢獻。 此外對於每次轉移,原來的值還要除以 \(2\) ,於是可以得出 \(f_{n,m}=\sum_{i=1}^m \frac{ik\binom{n-i-1}{m-i}}{2^{n-i}}\) 。直接計算即可,此外在 \(n=m\) 時需要特判答案為 \(nk\) ,複雜度 \(O(n)\) 。
程式碼
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mk make_pair
//#define int LL
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 1000010;
LL T, N, M, K, two[maxn], fact[maxn], invfact[maxn], inv[maxn], B = 500000004;
LL qpow(LL a, LL x, LL m)
{
LL ans = 1;
while (x)
{
if (x & 1)
ans = ans * a % m;
x >>= 1;
a = a * a % m;
}
return ans % m;
}
void inv_init(LL n, LL m)
{
inv[1] = 1;
for (LL i = 2; i <= n; i++)
{
LL j = m % i;
inv[i] = (-inv[j] * (m / i) % m + m) % m;
}
}
void fact_init(LL n, LL m)
{
fact[0] = fact[1] = 1;
for (LL i = 2; i <= n; i++)
fact[i] = fact[i - 1] * i % m;
invfact[n] = qpow(fact[n], m - 2, m);
for (LL i = n; i > 0; i--)
invfact[i - 1] = invfact[i] * i % m;
}
LL C(LL x, LL y)
{
if (x < 0 || y < 0 || x - y < 0)
return 0;
LL ans = 1;
for (LL i = 0; i < y; i++)
ans = ans * (x - i) % MOD;
ans = ans * invfact[y] % MOD;
return ans;
}
void solve()
{
LL ans = 0;
if (N == M)
ans = N * K % MOD;
else
{
LL tmp = C(N - 2, M - 1);
for (LL i = 1; i <= M; i++)
{
ans = (ans + tmp * i % MOD * K % MOD * two[N - i] % MOD) % MOD;
tmp = tmp * (M - i) % MOD * inv[N - i - 1] % MOD;
}
}
cout << ans << endl;
}
int main()
{
IOS;
fact_init(1000000, MOD), inv_init(1000000, MOD);
two[0] = 1, two[1] = B;
for (int i = 2; i <= 1000000; i++)
two[i] = two[i - 1] * B % MOD;
cin >> T;
while (T--)
{
cin >> N >> M >> K;
solve();
}
return 0;
}