转载请注明出处,谢谢http://blog.youkuaiyun.com/ACM_cxlove?viewmode=contents by---cxlove
题目:每个数,有4个评估
1、是素数 2、约数个数是素数 3、约数的和是素数 4、约数的乘积是完全平方数
从n个中选出k个,使得分数最高,如果选的k个中没有一个满足某个条件,则有另外的分值
http://acm.hdu.edu.cn/showproblem.php?pid=4610
首先因为数据范围不大,1-1e6,可以预处理,用到的数是20000个
第一次把1e6的所有数的各项评估都完成,直接TLE,N * sqrt(N)的复杂度,其实用到的只有2W,不需要全部预处理,打好素数表就行了。
第一项直接利用素数表,2、3项枚举约数后利用素数表,第4项不能直接保存乘积再判断,可以发现sqrt(n)枚举约数的时候,每次两个约数 i , n / i 他们的乘积是n,所以约数乘积是n ^ y * (n 是平方数? sqrt(n) : 1) 。
所以可以边乘边化简。
处理完之后,对于所有的数,可以分为2 ^ 4类。
第一次做法是:枚举哪几种评估不选,然后 贪心,WA,对拍后发现问题,这样选可能某个评估的值还是没有选到,但是额外值没有加入,而额外值又可能是负的。
第二次:枚举2 ^ 16种情况,表现选哪几种,要注意的时候,枚举了哪几种,那么就至少要取一个,否则还是会出现上面的情况。之后还是贪心就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1000001;
const int M = 1005;
bool isprime[N << 2] = {false}, flag[N][4] = {false} , vis[N] = {false};
int score[N] = {0} , state[N];
int n , k , b[4] , cnt[1 << 4] , get[1 << 4] , id[1 << 4];
bool issquare (LL num) {
LL t = sqrt(num + 0.0);
if(t * t == num || (t + 1) * (t + 1) == num || (t - 1) * (t - 1) == num) return true;
return false;
}
bool cmp (int a , int b) {
return get[a] > get[b];
}
void Init() {
for (int i = 0 ; i < N << 2; i ++) {
isprime[i] = true;
}
isprime[1] = false;
for (int i = 2 ; i < N << 2 ; i ++) {
if (! isprime[i]) continue;
for (int j = 2 ; i * j < N << 2 ; j ++) {
isprime[i * j] = false;
}
}
get[0] = 0;
for (int i = 1 ; i < 1 << 4 ; i ++) {
get[i] = get[i >> 1] + (i & 1);
}
for (int i = 0 ; i < 1 << 4 ; i ++) {
id[i] = i;
}
sort (id , id + (1 << 4) , cmp);
}
void check(int i) {
if (vis[i]) return ;
flag[i][0] = isprime[i];
int sum = 0 , cnt = 0;
LL product = 1LL;
for (int j = 1 ; j * j <= i ; j ++) {
if(i % j == 0) {
product = (LL)product * j;
sum += j;
cnt ++;
if (j * j != i) {
product *= i / j;
sum += i / j;
cnt ++;
}
if (product == (LL)i * i) {
product = 1LL;
}
}
}
flag[i][1] = isprime[cnt];
flag[i][2] = isprime[sum];
flag[i][3] = issquare(product);
for (int j = 0 ; j < 4 ; j ++) {
score[i] += flag[i][j] ? 1 : 0;
state[i] += (flag[i][j] ? 1 : 0) << j;
}
vis[i] = true;
}
int main () {
#ifndef ONLINE_JUDGE
freopen("input.txt", "r" , stdin);
#endif
Init();
int t;
scanf ("%d", &t);
while (t --) {
scanf ("%d %d", &n, &k);
for (int i = 0 ; i < 1 << 4; i ++) {
cnt[i] = 0;
}
for (int i = 0 ; i < n ; i ++) {
int a , b;
scanf ("%d %d", &a, &b);
check(a);
cnt[state[a]] += b;
printf("%d%c", score[a], i == n - 1 ? '\n' : ' ');
}
for (int i = 0 ; i < 4 ; i ++) {
scanf ("%d", &b[i]);
}
int ans = - (1 << 20);
for (int i = 0 ; i < 1 << 16 ; i ++) {
int remain = k , ret = 0 , cur = 0;
for (int j = 0 ; j < 1 << 4 ; j ++) {
if(! (i & (1 << id[j]))) continue;
if(cnt[id[j]]) {
remain --;
ret += get[id[j]];
}
else remain = -1;
}
if (remain < 0) continue;
for (int j = 0 ; j < 1 << 4 ; j ++) {
if(! (i & (1 << id[j]))) continue;
cur |= id[j];
if(remain && remain > cnt[id[j]] - 1) {
remain -= cnt[id[j]] - 1;
ret += (cnt[id[j]] - 1) * get[id[j]];
}
else {
ret += remain * get[id[j]];
remain = 0;
}
}
if (remain == 0) {
for (int j = 0 ; j < 4 ; j ++) {
if(! ((1 << j) & cur) )
ret += b[j];
}
ans = max (ans , ret);
}
}
printf ("%d\n", ans);
}
return 0;
}