10.21 考试总结

填坑ing

T1 card

神仙 czk 在休息时间和同学们玩卡牌游戏。 一共有 \(n\) 张卡牌,每张卡牌上有一个数字 \(A_i\),每次可以从中选出 \(k\) 张卡牌。一种选取方 案的幸运值为这 \(k\) 张卡牌上数的异或和。 神仙 czk 为了考倒你们,想知道所有选取方案的幸运值之和。 但是良心搬题人为了不为难大家,只需要你们输出所有选取方案的幸运值之和除以 \(998244353\) 的余数,

对于 30% 的数据 \(n\leq 20\) ,对于 100% 的数据 \(n\leq 10^5\)

30pts 随便状压一下就可以。

100pts

考虑把每一位分开来考虑。假设当前这一位上为 \(1\) 个数字个数为 \(num1\) ,那么 为零的就有 \(n-num1\)

对答案的贡献就是 \(\displaystyle\sum_{i=1, i 为奇数}^{k} C_{num1}^{i} \times C_{num0}^{k-i} \times 2 ^i\)

直接模拟就做完了。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5+10;
const int p = 998244353;
int n,m,x,ans;
int jz[N],inv[N],num[35][2],base[35];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
int ksm(int a,int b)
{
	int res = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a % p;
		a = a * a % p;
	}
	return res;
}
void YYCH()
{
	jz[0] = inv[0] = 1;
	for(int i = 1; i <= N-5; i++) jz[i] = jz[i-1] * i % p;
	inv[N-5] = ksm(jz[N-5],p-2);
	for(int i = N-6; i >= 1; i--) inv[i] = inv[i+1] * (i+1) % p;
}
int C(int n,int m)
{
	if(n < m) return 0;
	return jz[n] * inv[m] % p * inv[n-m] % p;
}
signed main()
{
	freopen("card.in","r",stdin);
	freopen("card.out","w",stdout);
	n = read(); m = read(); base[0] = 1; YYCH();
	for(int i = 1; i <= 32; i++) base[i] = base[i-1] * 2 % p;
	for(int i = 1; i <= n; i++)
	{
		x = read();
		for(int j = 0; j <= 32; j++)
		{
			int c = (x>>j) & 1;
			num[j][c]++; 
		}
	}
	for(int i = 0; i <= 32; i++)
	{
		for(int j = 1; j <= min(num[i][1],m); j += 2)
		{
			ans = (ans + C(num[i][1],j) * C(num[i][0],m-j) % p * base[i] % p) % p;
		}
	}
	printf("%lld\n",ans);
	fclose(stdin); fclose(stdout);
	return 0;
}

T2 赌注

Desprition

\(N\) 个庄家。你可以到庄家那边下注,每次可以猜大猜小,猜一次一元钱。每一次开彩前,你都可以到任意个庄家那里下赌注。如果开彩结果是大,你就可以得到你之前猜大的庄家相应的 \(a_i\) 元钱。如果开彩结果是小,你就可以得到你之前猜小的庄家相应的 \(b_i\) 元钱。你可以在同一个庄家那里既猜大又猜小(这样是两块钱),也可以什么都不猜(这样不用钱)。但是阴险狡诈的庄家会根据你下注的信息控制开彩的结果,让你赢的钱数尽量少。问怎么样下注,才能赢走最多的钱。输出一个四位小数。

对于 100% 的数据 \(n\leq 10^5\)

sloution

假设我们现在猜大可以得到 \(sum1\) 元,猜小可以得到 \(sum2\) 元,一共猜了 \(x+y\) 次。

那么答案就是 \(max(sum1-sum2) - x-y\). 我们要最大化这个价值。

很显然,当猜的次数一定的时候,我们希望 \(sum1,sum2\) 尽可能的大。

所以把 \(a_i,b_i\) 分别按从小到大排一遍序,处理个前缀和。

用双指针扫一遍就能得到答案。

考试的时候没注意到边界 \(100pts -> 70pts\)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int n;
double a[N],b[N],ans;
bool comp(double a,double b)
{
	return a > b;
}
int main()
{
	freopen("coin.in","r",stdin);
	freopen("coin.out","w",stdout);
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%lf%lf",&a[i],&b[i]);
	}
	sort(a+1,a+n+1,comp);
	sort(b+1,b+n+1,comp);
	for(int i = 1; i <= n; i++) a[i] = a[i-1] + a[i];
	for(int i = 1; i <= n; i++) b[i] = b[i-1] + b[i];
	int l = 1;
	for(int i = 1; i <= n; i++)
	{
		if(l > n) break;
		while(l <= n && b[l] <= a[i]) l++;
		if(b[l] > a[i] || l > n) l--;
		ans = max(ans,b[l] - (i + l));
	}
	l = 1;
	for(int i = 1; i <= n; i++)
	{
		if(l > n) break;
		while(l <= n && a[l] <= b[i]) l++;
		if(a[l] > b[i] || l > n) l--;
		ans = max(ans,a[l] - (i+l));
	}
	printf("%.4lf",ans);
	fclose(stdin); fclose(stdout);
	return 0;
}
上一篇:dd


下一篇:「JLOI2015」骗我呢 解题报告?