两个人玩游戏,每回合一方可以放另一方没有放过的卡,谁最后没卡放谁输
每个人优先出的牌的颜色肯定是场上没出过的, 对方也持有的, 并且两个人手中持有数量最多的牌.对方持有的越多意味着可以封掉更多的牌, 而自己手里的越多意味着可以防止自己更多的牌被封掉.因此, 对所有两个人手里都持有的颜色的牌数进行统计, 从大到小依次分配给第一, 第二个玩家.如果此时第一个玩家手里的牌数 > 第二个玩家, 则第一个玩家胜利, 否则第二个玩家胜利.到此为止, 问题转换成另一个问题, 就是有一堆东西, 每个东西有两个值, A 拿到这个东西的收益是 a i a_i ai, B B B 拿到的收益是 b i b_i bi.两人依次拿.求最优策略下两人的各自收益.这是一个经典问题, 答案就是按照 a i + b i a_i+b_i ai+bi, 排序模拟一下就好了.
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;
}
对于每一个等级,可以花 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 t−1 次都是失败的,所以下一状态的花费为当前状态的花费 + 成功的花费 + 失败的花费 + 失败后再次回到当前状态的花费。于是:
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]+(t−1)×a[i]+(t−1)×(dp[i]−dp[xi])
又 t − 1 t = 1 − r i s i \frac{t−1}{t}=1−\frac{r_i}{s_i} tt−1=1−siri,即 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]+(risi−1)×(dp[i]−dp[xi])
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;
}