为了能到远方,脚下的每一步都|

Aurora-JC

园龄:3年粉丝:3关注:4

2023-10-04 14:41阅读: 351评论: 0推荐: 0

高维前缀和 (SOSDP)

介绍

一维前缀和 : s[i]=s[i1]+a[i]
二维前缀和: s[i][j]=s[i][j1]+s[i1][j]s[i1][j1]

当然也可以这么写:

for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
a[i][j] += a[i - 1][j];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
a[i][j] += a[i][j - 1];

n 维前缀和, 我们可以做 n 次一维前缀和,差不多这个样子, 跟二维前缀和是类似的。

for(int i[1] = 1; i[1] <= n; i[1]++)
for(int i[2] = 1; i[2] <= n; i[2]++)
//...
a[i[1]][i[2]][i[3]] ... += a[i[1] - 1] ...;
for(int i[1] = 1; i[1] <= n; i[1]++)
for(int i[2] = 1; i[2] <= n; i[2]++)
//...
a[i[1]][i[2]][i[3]] ... += a[i[1]][i[2] - 1] ...;
...

例题

Ⅰ. P5495 Dirichlet 前缀和

根据唯一分解定理,将每个素数看成一维,然后相当于是子集和,用高维前缀和即可。状态压缩,可以仔细思考。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e7 + 67;
int read(){
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
#define uint unsigned int
uint seed;
inline uint getnext(){
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
return seed;
}
bool _u;
int n;
uint b[N], ans;
bool ispri[N];
bool _v;
int main(){
cerr << abs(&_u - &_v) << " MB\n";
n = read(), seed = read();
for(int i = 1; i <= n; ++i) b[i] = getnext();
ispri[1] = 1;
for(int i = 1; i <= n; ++i){
if(!ispri[i])
for(int j = i; j <= n; j += i)
b[j] += b[j / i], ispri[j] = 1;
ans ^= b[i];
}
printf("%lld\n", ans);
return 0;
}

Ⅱ. [ARC100E] Or Plus Max

将题目转成对于每个 Ki|j=K 的最大值,然后就是求子集前缀最大值。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = (1 << 19) + 67;
int read(){
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
bool _u;
int n;
ll ans;
struct node{
ll x, y;
node operator + (const node &p){
node z;
if(x > p.x) z.x = x, z.y = max(y, p.x);
else z.x = p.x, z.y = max(x, p.y);
return z;
}
}a[N];
bool _v;
int main(){
cerr << abs(&_u - &_v) / 1048576.0 << " MB\n";
n = read();
for(int i = 0; i < (1 << n); ++i) a[i].x = read(), a[i].y = -1e18;
for(int i = 0; i < n; ++i)
for(int j = 0; j < (1 << n); ++j)
if(j & (1 << i)) a[j] = a[j] + a[j ^ (1 << i)];
for(int i = 1; i < (1 << n); ++i){
ans = max(ans, a[i].x + a[i].y);
printf("%lld\n", ans);
}
return 0;
}

Ⅲ.CF1208F Bits And Pieces

我们可以枚举 di,就变成了找 bj&bk ,显然可以贪心从高位枚举到低位,而 & 的话就是求 超集 (与子集相反)。

#include<bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int N = 2e6 + 67;
int read(){
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
bool _u;
int n;
int a[N];
pii dp[N];
void add(int x, int id){
if(dp[x].fi < id) dp[x].se = dp[x].fi, dp[x].fi = id;
else dp[x].se = max(id, dp[x].se);
}
void merge(int x1, int x2){
if(x1 > 2e6 || x2 > 2e6) return ;
add(x1, dp[x2].fi), add(x1, dp[x2].se);
}
bool _v;
signed main(){
cerr << abs(&_u - &_v) / 1048576.0 << " MB\n";
memset(dp, -1, sizeof(dp));
n = read();
for(int i = 1; i <= n; ++i) a[i] = read(), add(a[i], i);
for(int i = 0; i < 21; ++i)
for(int j = 0; j <= 2e6; ++j)
if(!(j & (1 << i))) merge(j, j ^ (1 << i));
int ans = 0;
for(int i = 1; i <= n - 2; ++i){
int lim = (1 << 21) - 1;
int cur = a[i] ^ lim, res = 0;
for(int j = 20; ~j; --j)
if(cur & (1 << j) && dp[res ^ (1 << j)].se > i)
res ^= (1 << j);
ans = max(ans, res | a[i]);
}
printf("%d\n", ans);
return 0;
}

参考:https://zhuanlan.zhihu.com/p/651143987

本文作者:南风未起

本文链接:https://www.cnblogs.com/jiangchen4122/p/17741614.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Aurora-JC  阅读(351)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起