对于 [1,n][1,n] 的子集 AA,它的分数 ss 为
初始分数为0;
对于所有的
i
∈
A
i
∈
A
,
s
+
=
a
i
s
+
=
a
i
i \in Ai∈A,s+=a_is+=a i
i∈Ai∈A,s+=ais+=ai
对于所有满足
i
,
j
∈
{
x
∈
A
∣
x
≥
2
}
,
i
k
=
j
(
k
∈
{
x
∈
Z
∣
x
≥
2
}
)
i
,
j
∈
x
∈
A
∣
x
≥
2
,
i
k
=
j
(
k
∈
x
∈
Z
∣
x
≥
2
)
的
二
元
组
(
i
,
j
)
(
i
,
j
)
,
s
−
=
b
j
s
−
=
b
j
i,j \in \{x \in A|x \ge 2 \},i^k=j(k \in \{x \in Z|x \ge 2\})i,j∈{x∈A∣x≥2},i k=j(k∈{x∈Z∣x≥2}) 的二元组 (i,j)(i,j),s-=b_js−=b_j
i,j∈{x∈A∣x≥2},ik=j(k∈{x∈Z∣x≥2})i,j∈x∈A∣x≥2,ik=j(k∈x∈Z∣x≥2)的二元组(i,j)(i,j),s−=bjs−=bj
请求出分数最高的子集的分数是多少。
哎,比赛的时候没有看到这道题目,没想到就是一道暴力水题
我们可以发现,对于一个数它不是任何数的幂次方,那么这个数一定是要加进来的
所以一开始就可以预处理出所有的数的幂次方,并且打上标记。
处理完没有打上标记的数字之后,我们就可以计算
先考虑一个性质:选2的次幂的答案,对选3的次幂的答案是没有影响的
这很显然,因为没有哪两个数的次幂是一个数
这样我们就可以单个处理每个数,并且和它的次幂
dfs暴力枚举这个数以及它的次幂要不要取来,每次算的时候就可以算出每个数的代价,然后单个数算完的时候就可以更新答案了。
我们来计算一下时间复杂度
没有打过标记的数最多有 √ n √n √n个数
然后每次dfs的时间复杂度为:
假设枚举的数是2 ,最大的小于n的次幂是 Q
l
o
g
2
n
<
=
Q
log_2 n<=Q
log2n<=Q
最多枚举2Q<=n
所以时间复杂度为 O ( n √ n O(n√n O(n√n
#include<bits/stdc++.h>
#define maxn 100010
#define int long long
using namespace std;
inline int read()
{
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch&15);ch=getchar();}
return res*f;
}
int ans,sum;
int p[maxn];
int t[maxn][150];
int num[maxn];
int vis[maxn];
int n,a[maxn],b[maxn];
inline void dfs(int x,int k)
{
if(k==num[x]+1)
{
int sumr=0;
for(int i=1;i<=num[x];i++)
{
if(vis[i])
{
sumr+=a[t[x][i]];
for(int w=i*2;w<=num[x];w+=i)
{
if(vis[w])
sumr-=b[t[x][w]];
}
}
}
sum=max(sum,sumr);
return ;
}
vis[k]=0;dfs(x,k+1);
vis[k]=1;dfs(x,k+1);
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
for(int i=2;i*i<=n;i++)
{
if(!p[i])
{
for(int j=i;j<=n;j*=i)
p[j]=1,t[i][++num[i]]=j;
}
}
for(int i=1;i<=n;i++)if(!p[i])ans+=a[i];
for(int i=2;i*i<=n;i++)
{
if(num[i])
{
sum=0;
dfs(i,1);
ans+=sum;
}
}
cout<<ans;
return 0;
}