CF1614 - Codeforces Round 757 (Div. 2)
C. Divan and bitwise operations
题意
存在一个长度为 的正整数序列 , 个限制条件,每个限制条件由 构成,表示 在区间 中的元素或运算值为 。对于任意一个满足该条件的序列,求该序列的所有子序列的异或值之和,结果对 取模。
数据范围:。
思路
开始自己从每一位 的取值去想的,结果发现根本无法求出所有子序列的异或值之和,看了题解才发现这题巨妙(
既然不容易求出每个子序列然后进行异或再求和,那就直接考虑异或运算下的子序列对答案的贡献。
考虑二进制位的第 位,如果所有的 或起来这一位都是 ,那么就不存在 的第 为 ,
现在考虑存在第 位为 的 ,令集合 为所有 第 位为 , 为所有 第 位为 ,则 ,
直接从子序列角度分析,一个子序列可以视为分别从集合 和集合 中取出元素,按照原有顺序,构成子序列,所以一个子序列的第 位异或值为 ,当且仅当,它选取了奇数个 中的元素,也就是
如果 为奇数,则 ,否则 。利用二项式系数的递推式,可以得到
于是第 位为 的子序列个数为
故,无论 中元素如何,只要 ,第 位为 的子序列的个数都为定值 。
等价于所有 或起来第 位为 ,那么对所有子序列求和,答案就是
#include <bits/stdc++.h>
#define db double
#define ll long long
#define int ll
#define vi vector<int>
#define vii vector<vi >
#define pii pair<int, int>
#define vp vector<pii >
#define vip vector<vp >
#define mkp make_pair
#define pb push_back
#define Case(x) cout << "Case #" << x << ": "
using namespace std;
const int INF = 0x3f3f3f3f;
const int P = 1e9 + 7;
int ksm(int a, int b) {int ret = 1; while (b) {if (b & 1) ret = ret * a % P; a = a * a % P; b >>= 1;} return ret;}
signed main(){
#ifdef _DEBUG
// FILE *file = freopen("out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n, m, tot = 0;
cin >> n >> m;
vi a(m);
for (int i = 0; i < m; i++) {
int l, r, x;
cin >> l >> r >> x;
tot |= x;
}
cout << tot * ksm(2, n-1) % P << '\n';
}
return 0;
}
D1. Divan and Kostomuksha (easy version)
题意
给出一个序列 ,可以对该序列进行重排,求重排后
的最大值。
数据范围:
思路
明显是dp问题,但不好想状态,利用 具有的单调性,为了便于转移,考虑当前序列最后一个 的值为dp变量。
考虑 为 满足 的序列的 的最大值。所以每一个 会确定至少一个长度为 的数列,满足 。
令 为含有因数 的 的个数。
由于每一个 可以通过 ( 为素数)转移过来(从数列上具体来说,是将因数为 的数放在 已有序列的末尾,重复的数字不变位置),最大值变化即
是为了避免重复计算相同因数 对 的贡献(因为在 中已经计算过了,重复的数位置不变)。
初始化: (将以 为因数的数排成一个数列)
先用 筛预处理素数,便于状态转移时直接使用。
总复杂度:
#include <bits/stdc++.h>
#define db double
#define ll long long
#define int ll
#define vi vector<int>
#define vii vector<vi >
#define pii pair<int, int>
#define vp vector<pii >
#define vip vector<vp >
#define mkp make_pair
#define pb push_back
#define Case(x) cout << "Case #" << x << ": "
using namespace std;
const int INF = 0x3f3f3f3f;
const int P = 998244353;
const int M = 5e6 + 10;
int cnt[M], t[M], f[M];
vi prim;
bool vis[M];
void Euler(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) prim.pb(i);
for (int j : prim) {
int k = i * j;
if (k > M) break;
vis[k] = 1;
if (i % j == 0) break;
}
}
}
signed main(){
#ifdef _DEBUG
// FILE *file = freopen("out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
Euler(5e6);
//cout << prim.size() << '\n'; // 348513
int n;
cin >> n;
vi a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
t[a[i]]++;
}
for (int i = 1; i < M; i++) {
for (int j = 1; j * i < M; j++) {
cnt[i] += t[j * i];
}
}
for (int i = 1; i < M; i++) f[i] = cnt[i] * i;
for (int i = M-1; i >= 1; i--) {
for (int j : prim) {
int k = i * j;
if (k >= M) break;
f[i] = max(f[i], f[k] + i * (cnt[i] - cnt[k]));
}
}
int ans = 0;
for (int i = 1; i < M; i++) {
if (cnt[i] == n) {
ans = max(ans, f[i]);
}
}
cout << ans << '\n';
return 0;
}