C、
题面
结论
n n n个元素的子集的异或和 = 2 n − 1 ∗ A =2^{n-1}*A =2n−1∗A
其中 A A A是所有元素按位或起来的结果
思路
题目保证每个位置上的元素都会被包含在内至少一次;
按位考虑;
如果某元素的某一位是 1 1 1,我们把这个元素(记为 x x x)抽出来;
那么剩下的 n − 1 n-1 n−1个元素,子集有 2 n − 1 2^{n-1} 2n−1;
子集中这一位要么是 0 0 0,要么是 1 1 1;
如果是 0 0 0的话,我们就选上 x x x,那么就得 1 1 1了;
如果是 1 1 1,那么我们不选 x x x,那么还是 1 1 1;
因此如果某一位是 1 1 1,那么贡献为 2 n − 1 2^{n-1} 2n−1
因此我们只需要将所有的数按位或起来,再乘上贡献就是答案了;
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
int qpow(int x,int y){
ll base = x,ret = 1;
while(y){
if(y&1){
ret *= base;
ret %= MOD;
}
base *= base;
base %= MOD;
y>>=1;
}
return ret;
}
void solve(){
int n,m;
cin >> n >> m;
int l,r,x;
ll tot = 0;
while(m--){
cin >> l >> r >> x;
tot |= x;
}
cout << (tot%MOD * qpow(2,n-1)%MOD) << '\n';
}
int main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
D1、
题面
思路
不难想到
d
p
(
1
)
=
n
dp(1)=n
dp(1)=n;
而 d p dp dp是从小的状态往大的状态转移;
时间复杂度 O ( m l o g m ) O(mlogm) O(mlogm)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10,M = 5e6 + 1;
#define int ll
int cnt[M + 5];//cnt(i) 表示是i的倍数的元素个数
int dp[M + 5];//dp(i) 表示以 gcd = i 开头,能得到的最大值
void solve(){
int n;
cin >> n;
for(int i=1,x;i<=n;++i){
cin >> x;
++cnt[x];
}
for(int i=1;i<M;++i)
for(int j=2*i;j<M;j+=i)
cnt[i] += cnt[j];
dp[1] = n; //将数值1放在开头 那么答案必然是数的个数
int ans = 0;
for(int y=1;y<M;++y){
for(int x=2*y;x<M;x+=y){
dp[x] = max(dp[x],dp[y] + (x-y) * cnt[x]);
}
ans = max(ans,dp[y]);
}
cout << ans << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
solve();
return 0;
}
D2、
题面
思路
比起D1来说, a i a_i ai的值变大了;
这个优化的思想跟普通埃筛
和优化埃筛
是一样的;
首先看一下普通埃筛和优化埃筛的代码;
//普通埃筛 nlogn
for(int i=2;i<=n;++i){
if(!vis[i]){
primes.push_back(i);
}
for(int j=2*i;j<=n;j+=i){
vis[j] = 1;
}
}
//优化埃筛 nloglogn
for(int i=2;i<=n;++i){
if(!vis[i]){
primes.push_back(i);
for(int j=2*i;j<=n;j+=i){
vis[j] = 1;
}
}
}
在上面我们是枚举了所有的倍数;
但是实际的转移有很多的合数的重复的、不必要的;
因此我们可以只从质数转移过来,就像优化版埃筛一样;
优化了两个地方,一个是计算cnt
,另一个是转移dp
;
这样处理的时间复杂度是
O
(
m
l
o
g
l
o
g
m
)
O(mloglogm)
O(mloglogm),见Code1
;
然后我觉得cnt
数组的转移有点抽象;
我们可以分解因子来处理cnt
,也是可以过的,不过稍微慢一点;
时间复杂度是 O ( n m ) O(n\sqrt{m}) O(nm),见Code2
Code1
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10,M = 2e7 + 1;
#define int ll
int cnt[M + 5];//cnt(i) 表示是i的倍数的元素个数
int dp[M + 5];//dp(i) 表示以 gcd = i 开头,能得到的最大值
bool st[M];
vector<int> primes;
void init(){
for(int i=2;i<M;++i){
if(!st[i]) primes.push_back(i);
for(auto p : primes){
if(1ll * p * i > M) break;
st[p*i] = 1;
if(i % p == 0) break;
}
}
}
void solve(){
init();
int n;
cin >> n;
for(int i=1,x;i<=n;++i){
cin >> x;
++cnt[x];
}
//因为越小的数倍数的个数越多
//因此我们从大往小转移
for(auto p : primes)
for(int j=M/p;j>=1;--j)
cnt[j] += cnt[j * p];
/*for(int i=1;i<M;++i)
for(int j=2*i;j<M;j+=i)
cnt[i] += cnt[j];*/
dp[1] = n; //将数值1放在开头 那么答案必然是数的个数
int ans = 0;
for(int y=1;y<M;++y){
for(auto p : primes){
if(p * y >= M) break;
int x = p*y;
dp[x] = max(dp[x],dp[y] + (x-y) * cnt[x]);
}
ans = max(ans,dp[y]);
}
/*for(int y=1;y<M;++y){
for(int x=2*y;x<M;x+=y){
dp[x] = max(dp[x],dp[y] + (x-y) * cnt[x]);
}
ans = max(ans,dp[y]);
}*/
cout << ans << '\n';
}
signed main(){
//std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
solve();
return 0;
}
Code2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10,M = 2e7 + 1;
ll cnt[M + 5];//cnt(i) 表示是i的倍数的元素个数
ll dp[M + 5];//dp(i) 表示以 gcd = i 开头,能得到的最大值
bool st[M];
vector<int> primes;
void init(){
for(int i=2;i<M;++i){
if(!st[i]) primes.push_back(i);
for(auto p : primes){
if(1ll * p * i > M) break;
st[p*i] = 1;
if(i % p == 0) break;
}
}
}
void solve(){
init();
int n;
cin >> n;
for(int i=1,x;i<=n;++i){
cin >> x;
for(int j=1;j*j<=x;++j){
if(x%j == 0){
++cnt[j],++cnt[x/j];
if(j == x/j) --cnt[x/j];
}
}
//++cnt[x];
}
//因为越小的数倍数的个数越多
//因此我们从大往小转移
/*for(auto p : primes)
for(int j=M/p;j>=1;--j)
cnt[j] += cnt[j * p];*/
/*for(int i=1;i<M;++i)
for(int j=2*i;j<M;j+=i)
cnt[i] += cnt[j];*/
dp[1] = n; //将数值1放在开头 那么答案必然是数的个数
ll ans = 0;
for(int y=1;y<M;++y){
for(auto p : primes){
if(p * y >= M) break;
int x = p*y;
dp[x] = max(dp[x],dp[y] + (x-y) * cnt[x]);
}
ans = max(ans,dp[y]);
}
/*for(int y=1;y<M;++y){
for(int x=2*y;x<M;x+=y){
dp[x] = max(dp[x],dp[y] + (x-y) * cnt[x]);
}
ans = max(ans,dp[y]);
}*/
cout << ans << '\n';
}
signed main(){
//std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
solve();
return 0;
}