CF 767Div1 D2. Game on Sum (Hard Version)

Problem - D2 - Codeforces

考虑dp,dp(n, m)表示还有n步操作,Bob至少还需要进行m步add操作的最终答案。

则假设当前Alice选择了一个x(x belong to [0, k]),那么Bob有两种走法:(假设当前不在边界情况)

1) 用减, 则答案为dp(n - 1, m) - x

2) 用加, 则答案为dp(n - 1, m - 1) + x

注意到dp(n - 1, m) > dp(n - 1, m - 1)

因此可以分别画出dp(n - 1, m) - x 和dp(n - 1, m - 1) + x的图像,则是一个"X”型,显然Alice会选择交点的那个x值,则发现dp(n, m) = (dp(n - 1, m) + dp(n - 1, m - 1)) / 2

考虑边界情况,dp(i, 0) = 0, dp(i, i) = k * i

之后考虑令f(n, m) = dp(n, m) * (2 ^ n) ,把n看成x轴、m看成y轴,发现f(n, m)的转移相当于要么往左下(-1,-1)方向走,要么往左(-1,0)走,然后考虑反方向推,即考虑从各个边界位置走到(n, m)方案,则因为dp(i, 0) = 0不用考虑,只用考虑(i, i)向右上或者向右,且不经过其它边界点,走到(n, m)方案数,发现此时相当于第一步一定要向右走,之后向右上或者向右任意,故减去第一步后总共需要走(n - i - 1) 步,其中(m - i)步要是右上方,得到此时贡献即为 C(n - i - 1, m - i)

下面的代码是之前用另一种奇奇怪怪的方法推的,不过也差不多

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
//#define int long long
using namespace std;
const int N = 2e6 + 100;
const int inf = 1e9;
//const ll inf = 1e18;
const ll mod = 1e9 + 7;

#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
	cerr << " " << to_string(H);
	debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

ll fac[N], pw[N], ifac[N], ipw[N];;

ll qpow(ll x, ll y = mod - 2)
{
	ll res = 1;
	while(y)
	{
		if(y & 1)
			res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

void init()
{
	int n = 2e6;
	fac[0] = 1;
	for(int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i % mod;
	ifac[n] = qpow(fac[n]);
	for(int i = n - 1; i >= 0; i--)
		ifac[i] = ifac[i + 1] * (i + 1) % mod;
	pw[0] = 1;
	for(int i = 1; i <= n; i++)
		pw[i] = pw[i - 1] * 2 % mod;	
	ipw[0] = 1;
	for(int i = 1; i <= n; i++)
		ipw[i] = ipw[i - 1] * (ll)(5e8 + 4) % mod;
}

ll C(ll x, ll y)
{
	if(x < y)
		return 0;
	if(y < 0)
		return 0;
	return fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}

void sol()
{
	int n, m, k;
	cin >> n >> m >> k;
	ll ban = 0, ans = 0;
	for(int i = 1; i <= m; i++)
	{
		ll val = i * 2 - 1;
		ll coef = C(n - 1, m - i);
		//cerr << val << ' ' << coef << '\n';
		ans = (ans + val * coef) % mod;
	}
	cout << ans * k % mod * ipw[n - 1] % mod << '\n';
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	init();
	int tt;
	cin >> tt;
	while(tt--)
		sol();
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值