D-XOR of Suffix Sums_2024牛客暑期多校训练营1 (nowcoder.com)
思路:
突破点:值域范围不大,可用树状数组维护
如果直接维护后缀,那么每一次增加一个数的进位会使之前维护的二进制串失效,注意到 s u f [ i ] = p r e [ n ] − p r e [ i − 1 ] suf[ i ] = pre[ n ] - pre[i - 1] suf[i]=pre[n]−pre[i−1],每次新增一个数的 p r e [ n ] pre[ n ] pre[n]是易得的, p r e [ i − 1 ] pre[ i - 1 ] pre[i−1]是已知的,所以我们可以维护前缀,答案为 ( p r e [ n ] − p r e [ 1 ] ) ⊕ ( p r e [ n ] − p r e [ 2 ] ) … … (pre[ n ] - pre[ 1 ]) \oplus \ (pre[ n ] - pre[ 2 ]) …… (pre[n]−pre[1])⊕ (pre[n]−pre[2])……
考虑二进制枚举,模数是 2 21 2^{21} 221,只有低21位有效,对于每一个位只要知道上述表达式出现1的奇偶性即可,问题抽象为在 a + b a + b a+b中,固定a, a + b a+b a+b有没有第i位对b来说是一个连续的区间,忽略大于i的位,可以用树状数组维护b,也可以理解为权值线段树,即维护 − p r e [ i ] -pre[i] −pre[i]。
考虑复杂度,每次最多加一个点,即最多有5e5个点,删除也不会超过这个数,所以可以暴力遍历要删的东西从树状数组从删除。
AC码:
#include <bits/stdc++.h>
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 3e6 + 50;
const ll mod = 2097152;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
ll a[N], tr[25][N];
ll two[30];
ll lowbit(int x) { return x & -x; }
void add(ll x, int op) {
for(int i = 0; i <= 20; i++){
int xx = (two[i + 1] - x % two[i + 1]) % two[i + 1];
if (xx == 0) {
tr[i + 1][0] += op;
continue;
} while (xx <= two[i + 1]) {
tr[i + 1][xx] += op;
xx += lowbit(xx);
}
}
}
ll quer(ll x, ll i) {
int ans = 0;
while (x > 0) {
ans += tr[i + 1][x];
x -= lowbit(x);
}
return ans;
}
ll query(ll x) {
ll res = 0, ans = 0;
for (int i = 0; i <= 20; ++i) {
int xx = x % two[i];
if (x >> i & 1) {
res = quer(two[i] - 1 - xx, i) + quer(two[i + 1], i) - quer(two[i + 1] - 1 - xx, i) + tr[i + 1][0];
} else {
res = quer(two[i + 1] - 1 - xx, i) - quer(two[i] - 1 - xx, i);
}
if (res & 1) ans |= 1ll << i;
}
return ans;
}
void work() {
int q; cin >> q;
int n = 0;
add(0, 1);
for (int i = 1; i <= q; ++i) {
ll t, v; cin >> t >> v;
for (int j = 1; j <= t; ++j) {
add(a[n], -1);
a[n] = 0;
n--;
}
n++;
a[n] = (a[n - 1] + v) % mod;
cout << query(a[n]) << '\n';
add(a[n], 1);
}
}
signed main() {
io;
two[0] = 1;
for (int i = 1; i <= 21; ++i) {
two[i] = two[i - 1] * 2;
}
int t = 1;
// cin >> t;
while (t--) {
work();
}
return 0;
}
/*
4
0 15
1 9
0 5
0 14
15
9
11
1
*/
G-The Set of Squares_2024牛客暑期多校训练营2 (nowcoder.com)
思路:
突破点:注意到1000以内的数包含的质数可以分为大质数(最多只能出现一次)和小质数(可以重复出现),按照大质数分组遍历,对小质数的状态进行状压背包。
定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示在第i个位置,当前大质数的奇偶性(j为1是奇),状态为k时的答案。
每次加入一个数t,令t的质数状态为st,转移方程为:
d p [ i ] [ j ] [ k ⊕ s t ] + = d p [ i − 1 ] [ j ] [ k ⊕ t ] dp[i][j][k\oplus st] += dp[i - 1][j][k\oplus t] dp[i][j][k⊕st]+=dp[i−1][j][k⊕t](不选第i个数,直接由上一个数转移来) + d p [ i − 1 ] [ j ⊕ 1 ] [ k ] × t +dp[i - 1][j \oplus 1][k] \times t +dp[i−1][j⊕1][k]×t中有的平方质数的贡献(作为t的单独贡献) [ 1 ] × k & s t ^{[1]}\times \ k \& st [1]× k&st 状态下包含的质数积(作为tk的共同贡献) [ 2 ] × ^{[2]}\times [2]× 大质数(当j从1变为0时)。
上述 [ 1 ] [ 2 ] [1][2] [1][2]式都可以预处理出来,转移时直接用。
每一个dp状态下都包含了全部(选或不选任何一个位置)情况的答案,队友说我讲不清楚,还是自己悟一下吧。
最后的 d p [ n ] [ 0 ] [ 0 ] − 1 dp[n][0][0] - 1 dp[n][0][0]−1(减去什么都不选的空集)就是最终答案。
写法注意:
- 第一维可以滚动
- 处理时只加入小质数贡献,大质数我们在dp中单独算,而且需要处理的下标范围不一样需要注意
- 在每一组dp结束后要清空没用的东西
- 如果当前所在的组是无大质数组,那么所有的 d p [ i ] [ 1 ] [ k ] dp[i][1][k] dp[i][1][k]都要计入 d p [ i ] [ 0 ] [ k ] dp[i][0][k] dp[i][0][k]。
AC码:
#include <bits/stdc++.h>
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2060;
const int M = 1000;
const ll mod = 1e9+7;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
bool is[N];
int tot, prime[N], a[N];
ll tval[N], state[N], cal[N];//i单独的贡献,i的状压状态,状态i表示的数
ll dp[2][2][N];
void euler_sieve() {
tot = 0;
prime[tot++] = 1;
for (int i = 2; i < N; ++i) {
if (!is[i]) prime[tot++] = i;//0为素数
for (int j = 1; j < tot && prime[j] * i < N; ++j) {
is[prime[j]*i] = 1;
if (i % prime[j] == 0)
break;
}
}
}
void init() {
for (int i = 0; i < N; ++i) tval[i] = cal[i]= 1;
for (int i = 1; i <= M; ++i) {
int x = i;
for (int j = 1; j <= 11; ++j) {
int p = prime[j];
while (x % p == 0) {
x /= p;
if ((state[i] >> (j - 1)) & 1) tval[i] = (tval[i] * p) % mod;
state[i] ^= (1 << (j - 1));
}
}
}
for (int i = 0; i < (1 << 11); ++i) {
for (int j = 11; j >= 1; --j) {
if (i >> (j - 1) & 1) cal[i] = (cal[i] * prime[j]) % mod;
}
}
}
void work() {
int n;
cin >> n;
vector<int> have[N];
for (int i = 1; i <= n; ++i) {
cin >> a[i];
int x = a[i];
for (int j = 1; j <= 11; ++j) {
int p = prime[j];
while ((x % p) == 0) {
x /= p;
}
}
if (x == 1) have[0].pb(a[i]);
else have[x].pb(a[i]);
}
int now = 1;
dp[now][0][0] = 1;
for (int i = 0; i <= 1000; ++i) {
if (have[i].size() == 0) continue;
for (auto t: have[i]) {
now ^= 1;
int x = state[t];
memset(dp[now], 0, sizeof(dp[now]));
for (int k = 0; k < (1 << 11); ++k) {
dp[now][0][k ^ x] = (dp[now][0][k ^ x] + dp[now ^ 1][0][k ^ x] + dp[now ^ 1][1][k] * tval[t] % mod * cal[k & x] % mod * (i == 0 ? 1 : i) % mod) % mod;
dp[now][1][k ^ x] = (dp[now][1][k ^ x] + dp[now ^ 1][1][k ^ x] + dp[now ^ 1][0][k] * tval[t] % mod * cal[k & x] % mod) % mod;
}
}
if (i == 0) {
for (int k = 0; k < (1 << 11); ++k) dp[now][0][k] = (dp[now][0][k] + dp[now][1][k]) % mod;
}
memset(dp[now][1], 0, sizeof(dp[now][1]));
}
cout << (dp[now][0][0] - 1 + mod) % mod << '\n';
}
signed main() {
io;
int t = 1;
// cin >> t;
euler_sieve();
init();
while (t--) {
work();
}
return 0;
}
/*
598
41 13 14 43 1 9 16 17 56 46 54 14 75 5 86 58 33 23 39 55 27 17 34 83 41 56 18 65 95 37 56 48 1 54 63 88 56 55 13 55 95 82 56 74 15 7 74 45 67 89 2 62 45 23 64 86 3 96 77 7 19 63 44 57 99 64 71 81 37 69 28 9 48 74 72 38 6 74 7 72 60 19 42 17 29 49 28 83 83 28 50 37 61 78 59 46 11 34 36 74 17 72 97 7 24 90 14 90 27 96 67 75 19 1 69 69 96 24 27 15 62 76 57 28 25 69 88 36 69 8 40 4 30 91 25 58 78 35 63 10 78 15 50 59 53 20 6 48 49 3 44 32 91 97 27 82 71 24 84 49 46 59 50 74 74 54 3 89 34 35 21 63 80 86 22 35 93 22 69 68 100 2 99 57 71 14 87 41 20 63 65 55 9 14 59 96 13 25 23 60 97 20 34 92 31 10 29 55 71 25 15 68 73 22 28 18 32 87 81 92 94 47 65 45 46 25 89 58 97 5 53 11 33 49 33 42 33 65 78 91 15 57 22 89 44 62 75 3 12 16 93 24 64 6 75 88 93 20 90 84 12 33 62 70 96 31 15 51 1 57 81 92 73 24 64 45 34 10 53 85 90 49 60 43 63 10 79 67 83 80 57 67 84 77 97 94 49 88 11 21 36 6 50 16 50 52 53 14 30 78 69 39 90 47 52 31 40 99 42 85 39 100 11 82 56 29 26 82 1 2 77 83 5 94 50 6 73 52 55 47 32 34 23 77 77 37 27 4 56 15 27 57 69 4 26 29 93 70 69 54 62 96 29 5 100 31 76 83 82 100 64 20 89 31 92 43 34 68 89 44 18 20 6 76 89 36 24 69 17 19 99 83 70 3 83 32 57 80 16 76 78 68 15 6 20 60 15 14 61 10 68 29 27 33 51 32 4 41 100 60 82 64 15 56 49 5 40 99 40 41 63 54 20 97 48 76 17 13 64 89 22 57 38 42 81 10 75 40 89 86 51 52 71 41 17 41 1 4 71 46 22 10 68 16 32 30 36 2 96 35 3 96 18 68 58 35 78 60 1 84 40 31 23 5 49 63 74 10 68 43 32 77 30 68 7 79 78 47 53 8 42 93 49 11 51 91 4 48 5 34 13 64 89 64 30 69 70 61 96 51 97 55 47 44 64 26 32 5 78 81 57 39 21 87 16 95 55 15 1 82 31 28 95 25 9 30 71 34 1 83 77 25 44 78 41 32 13 92 15 68 81 62 28 62 75 73 62 11 38 38 88 64 70 78 32 34 87 19 3 19 10 9 28 43 13 54 73 92 91 22 85 59 78 88 76 77 80 47
552436798
*/

65

被折叠的 条评论
为什么被折叠?



