题目大意:有一个长度为n的全排列数组s,和一个由0,1组成的字符串ss,ss[i]=0代表s的子序列t里没有i,1代表有,每次操作可以任选一个不大于n的整数k,在s中删除k的最小倍数,费用为k,问如果要把s变成t最小花费是多少
1<=n<=1e6,1<=k<=n
思路:对于s中的每个数,我们从1倍开始遍历他的倍数j,如果ss[i*j]=0,就在答案中加上i,同时给i*j做上标记,避免重复记录答案,如果遇到ss[i*j]=1,就直接退出循环去遍历下一个数
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
char tt[N];
bool cnt[N],cnts[N];
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
cnt[i] = 0;
cnts[i] = 1;
}
getchar();//读入换行符
for (int i = 1; i <= n; i++)
{
scanf("%c", &tt[i]);
if (tt[i] == '1')
{
cnt[i] = 1;//在t中出现
cnts[i] = 0;//在s中不需要删除
}
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j * i <= n; j++)
{
ll temp = i * j;//遍历i的所有倍数
if (cnt[temp])//如果当前倍数在t中,就不能删了
break;
if (cnts[temp] == 0)//这个点被删过了或不需要删除
continue;
cnts[temp] = 0;//标记删过的点
ans += i;
}
}
printf("%lld\n", ans);
}
return 0;
}