2023.10.29 题解

cf 1829H


题目大意:给定一个长度为n的数组a(0<=a[i]<=63, 1<=n<=2e5),整数k(0<=k<=6),
求a有多少个子序列的按位与(and)值的二进制中有k个1。

分析:a[i]<=63,说明任意子序列的and值也<=63,这样我们可以求出and值为0-63的所有子序列
数量,最后判断0-63有哪些的二进制里只有k个1的,把答案累加即可。
dp[i][j]表示前i个元素中and值为j的数量,转移方程很简单。

void solve() {
	int n, k;
	cin >> n >> k;

	vector<vector<int> > dp(n + 1, vector<int>(1 << 6));
	for (int i = 1; i <= n; i++) {
		int a;
		cin >> a;
		for (int mask = 0; mask < (1 << 6); mask++) {
			dp[i][mask] = (dp[i - 1][mask] + dp[i][mask]) % mod;
			dp[i][mask & a] = (dp[i - 1][mask] + dp[i][mask & a]) % mod;
		}
		dp[i][a] = (dp[i][a] + 1) % mod; //本身为1个新序列
	}
	ll ans = 0;
	for (int mask = 0; mask < (1 << 6); mask++) {
		if(__builtin_popcount(mask) == k) {
			ans = (ans + dp[n][mask]) % mod;
		}
	}

	cout << ans << "\n";
}
cf 1792D


题目大意:给你n个长度为m的排列(n<=4e5, 1<=m<=10),假设长度为 p1,p2,…,pm 的排列的美度
是 k 中最大的 p1=1,p2=2,…,pk=k。如果是p1≠1,那么美度就是0。
两个排列p⋅q的乘积是排列r,使得r[j]=q[p[j]]。
对于从1到n的每个i,打印从1到n的所有j的排列组合ai⋅aj的最大美度。(可能是i==j)。

假设p*q组成的r的美度为k,是不是可以得到p*q=(1,2...k,r[k+1]...)。
移项:p=(1,2...k,r[k+1]...)*q^(-1)。
可以看出q^(-1)和p的前k个数字相同,所以我们只需要找到p与其他所有排列的逆排列的最大公共前缀长度即可。
做法:先存好每一个排列的逆排列,排序后用lower_bound查找即可,最大公共前缀长度不是找到的,就是它的前一个。

int getres(vector<int> a, vector<int> b) {
	int res = 0;
	while(res < (int)a.size() && a[res] == b[res]) {
		res++;
	}
	return res;
}

void solve() {
	int n, m;
	cin >> n >> m;

	vector<vector<int> > a(n, vector<int>(m)), b = a;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> a[i][j];
			--a[i][j];
			b[i][a[i][j]] = j;
		}
	}

	sort(b.begin(), b.end());
	for (int i = 0; i < n; i++) {
		int j = lower_bound(b.begin(), b.end(), a[i]) - b.begin();

		int ans = 0;
		if(j > 0) ans = max(ans, getres(a[i], b[j - 1]));
		if(j < n) ans = max(ans, getres(a[i], b[j]));

		cout << ans << ' ';
	}
	cout << "\n";
}
cf 1770C


题目大意:有一个长度为n的数组a (n的总和不超过1000,1<=a[i]<=1e18),
问你是否存在一个正整数x,使得所有(1<=i, j<=n)都满足gcd(a[i] + x, a[j] + x) == 1。

对于给定的x,让我们记b[i]=a[i]+x。
条件「对于gcd(b[i],b[j])==1(1<=i,j<=n)恒成立」等价于「每个质数p只能整除至多一个b[i]」。
那么,对于一个质数p,我们能否判断p,是否总是整除至少两个b[i](无论x的取值)。
根据鸽巢原理,我们只需要枚举小于等于n/2的质数(枚举了合数也不会错)。
cnt[i]为i在(a[1]%p...a[n]%p)中出现的个数,当cnt中的最小值大于等于2时,我们就输出NO,
若对于所有质数都成立,则我们可以列出一个同余方程组,并使用中国剩余定理求解合适的x。

void  solve() {
	int n;
	cin >> n;

	vector<ll> a(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}

	sort(a.begin(), a.end());
	for (int i = 0; i < n - 1; i++) {
		if(a[i] == a[i + 1]) {
			cout << "NO\n";
			return;
		}
	}

	for (int mod = 2; mod <= n / 2; mod++) {
		vector<int> cnt(mod);
		for (int i = 0; i < n; i++) {
			cnt[a[i] % mod]++;
		}
		if(*min_element(cnt.begin(), cnt.end()) >= 2) {
			cout << "NO\n";
			return;
		}
	}

	cout << "YES\n";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值