2019HDU多校第七场

J Just Repeat

题意:

两个人玩游戏,每回合一方可以放另一方没有放过的卡,谁最后没卡放谁输

每个人优先出的牌的颜色肯定是场上没出过的, 对方也持有的, 并且两个人手中持有数量最多的牌.对方持有的越多意味着可以封掉更多的牌, 而自己手里的越多意味着可以防止自己更多的牌被封掉.因此, 对所有两个人手里都持有的颜色的牌数进行统计, 从大到小依次分配给第一, 第二个玩家.如果此时第一个玩家手里的牌数 > 第二个玩家, 则第一个玩家胜利, 否则第二个玩家胜利.到此为止, 问题转换成另一个问题, 就是有一堆东西, 每个东西有两个值, A 拿到这个东西的收益是 a i a_i ai, B B B 拿到的收益是 b i b_i bi.两人依次拿.求最优策略下两人的各自收益.这是一个经典问题, 答案就是按照 a i + b i a_i+b_i ai+bi, 排序模拟一下就好了.

AC代码:
const int N = 5e5 + 50;

int n, m, p;
int a[N], b[N];
int num[N], cnt1[N], cnt2[N];
int x, y;
unsigned long long k1, k2;

struct node
{
	int pos, he;
} ans[N];

bool cmp(node a, node b)
{
	if (a.he == b.he)
		return a.pos < b.pos;
	else
		return a.he > b.he;
}

unsigned long long rng()
{
	unsigned long long k3 = k1, k4 = k2;
	k1 = k4;
	k3 ^= k3 << 23;
	k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
	return (k2 + k4);
}

int main()
{
	int t;
	sd(t);
	while (t--)
	{
		sddd(n, m, p);
		rep(i, 0, n + m + 10)
			num[i] = 0;
		rep(i, 0, n + m + 10)
			cnt1[i] = 0;
		rep(i, 0, n + m + 10)
			cnt2[i] = 0;
		rep(i, 0, n + m + 10)
		{
			ans[i].pos = 0;
			ans[i].he = 0;
		}
		if (p == 1)
		{
			rep(i, 1, n)
				sd(a[i]);
			rep(i, 1, m)
				sd(b[i]);
		}
		else
		{
			int mo;
			scanf("%llu%llu%d", &k1, &k2, &mo);
			rep(i, 1, n)
				a[i] = (int)(rng() % mo);
			scanf("%llu%llu%d", &k1, &k2, &mo);
			rep(i, 1, m)
				b[i] = (int)(rng() % mo);
		}
		int cnt = 0;
		sort(a + 1, a + n + 1);
		sort(b + 1, b + n + 1);
		rep(i, 1, n)
		{
			num[++cnt] = a[i];
		}
		rep(i, 1, m)
		{
			num[++cnt] = b[i];
		}
		sort(num + 1, num + cnt + 1);
		int sum = unique(num + 1, num + cnt + 1) - num - 1;
		int maxn = 0;
		rep(i, 1, n)
		{
			x = lower_bound(num + 1, num + sum + 1, a[i]) - num;
			//pd(x);
			maxn = max(x, maxn);
			cnt1[x]++;
		}
		rep(i, 1, m)
		{
			x = lower_bound(num + 1, num + sum + 1, b[i]) - num;
			//pd(x);
			maxn = max(x, maxn);
			cnt2[x]++;
		}
		int now = 0, cnta = 0, cntb = 0;
		rep(i, 0, sum)
		{
			if (cnt1[i] > 0 && cnt2[i] > 0)
			{
				ans[++now].pos = i;
				ans[now].he = cnt1[i] + cnt2[i];
			}
			else if (cnt1[i] > 0)
			{
				cnta += cnt1[i];
			}
			else if (cnt2[i] > 0)
			{
				cntb += cnt2[i];
			}
		}
		sort(ans + 1, ans + 1 + now, cmp);
		int A = 0, B = 0;
		rep(i, 1, now)
		{
			if (i & 1)
				A += cnt1[ans[i].pos];
			else
				B += cnt2[ans[i].pos];
		}
		//pdd(cnta + A, cntb + B);
		if ((cnta + A) > (cntb + B))
			puts("Cuber QQ");
		else
			puts("Quber CC");
	}
	return 0;
}

K Kejin Player

题意:

对于每一个等级,可以花 a i a_i ai 元,有 p i p_i pi 概率升级,如果升级失败就退到 x i x_i xi 级,问从 l i l_i li 级升到 r i r_i ri 级的钱数期望

g ( l , r ) g(l,r) g(l,r) l l l 升到 r r r 的期望,这种期望满足减法 g ( l , r ) = g ( 1 , r ) − g ( 1 , l ) g(l,r)=g(1,r)−g(1,l) g(l,r)=g(1,r)g(1,l)。因为升级只能一级一级升, 所以要从 1 1 1 升级到 r r r, 必然要经过 l l l。可以降维,用 d p [ i ] dp[i] dp[i] 表示从 1 1 1 升到 i i i 的期望,则 g ( l , r ) = d p [ r ] − d p [ l ] g(l,r)=dp[r]−dp[l] g(l,r)=dp[r]dp[l]

d p [ i ] dp[i] dp[i] 转移至 d p [ i + 1 ] dp[i+1] dp[i+1],假设尝试了 t t t 次才成功,那么也就是前面 t − 1 t−1 t1 次都是失败的,所以下一状态的花费为当前状态的花费 + 成功的花费 + 失败的花费 + 失败后再次回到当前状态的花费。于是:

d p [ i + 1 ] = d p [ i ] + 1 × a [ i ] + ( t − 1 ) × a [ i ] + ( t − 1 ) × ( d p [ i ] − d p [ x i ] ) dp[i+1]=dp[i]+1×a[i]+(t−1)×a[i]+(t−1)×(dp[i]−dp[x_i]) dp[i+1]=dp[i]+1×a[i]+(t1)×a[i]+(t1)×(dp[i]dp[xi])

t − 1 t = 1 − r i s i \frac{t−1}{t}=1−\frac{r_i}{s_i} tt1=1siri,即 t = s i r i t=\frac{s_i}{r_i} t=risi

于是状态转移方程为:

d p [ i + 1 ] = d p [ i ] + s i r i × a [ i ] + ( s i r i − 1 ) × ( d p [ i ] − d p [ x i ] ) dp[i+1]=dp[i]+\frac{s_i}{r_i}×a[i]+(\frac{s_i}{r_i}−1)×(dp[i]−dp[x_i]) dp[i+1]=dp[i]+risi×a[i]+(risi1)×(dp[i]dp[xi])

AC代码:
ll qpow(ll x, ll n, ll mod)
{
	ll res = 1;
	while (n)
	{
		if (n & 1)
			res = (res * x) % mod;
		x = x * x % mod, n >>= 1;
	}
	return res;
}

const int N = 5e5 + 50;
const int mod = 1e9 + 7;

int n, q;
ll r[N], s[N], x[N], a[N];
int L, R;
ll f[N];
int main()
{
	int T;
	sd(T);
	while (T--)
	{
		sdd(n, q);
		f[0] = 0;
		rep(i, 1, n)
		{
			sldd(r[i], s[i]);
			sldd(x[i], a[i]);
			ll inv = qpow(r[i], mod - 2, mod) % mod;
			ll t = (s[i] * inv) % mod;
			f[i + 1] = (f[i] + (t * a[i]) % mod + ((t - 1) * (f[i] - f[x[i]])) % mod + mod) % mod;
		}
		while (q--)
		{
			sdd(L, R);
			ll ans = (f[R] - f[L] + mod) % mod;
			pld(ans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值