A ,B虽然是水题,但要是没想清楚就容易掉进某个坑里。。
C:预处理出所有可能的和,然后每个每个和分给a b两个数
D:比赛的时候题目没看懂,题意就是说在一个n*n的正方形网格中分割k次有几种分法,每次分完后剩下的四个子矩形必须为正方形。考虑到最后可以分割log级别的次数,而且每分割一次,又出现了四个空白的子正方形,自然就可以想到dp,很明显的子结构啊。我们不妨从小到大构造,每多一层,正方形的大小就扩大两倍,dp[i][j]表示前i层,已经画了k次的总方案数,那么转移的时候我们只需要枚举当前层的四个角落各自画了多少次,然后变成一个两倍大小的正放形(多画一步)。自然不能四重循环去枚举,中间要套一个的d【】数组来多进行一次小DP,算是DP套着DP吧。
E:求满足p!%(a1!*a2!*a3!*...*ak!)=0;的最小的p。k的范围是100w,a[i]的范围是1000w.
没什么难想的,就是考你有没有掌握什么快速分解质因子的方法。。。。
学了一种欧拉筛法,,可以在线性时间内筛选出素数,还可以得到每个数的最小质因子lp[i]。
http://wenku.baidu.com/view/542961fdba0d4a7302763ad5.html
O(n)证明的关键是每个数只会被筛一次
然后接下来的问题就是怎么利用lp[i]来分解这100w个数,其实这一步的计算量跟数的个数无关,跟数的大小有关。
我们可以用一个数组cnt[i]表示i这个素数出现的次数,那么接下来这段代码就是精髓了。
for(int i = 0; i < n; i++) scanf("%d",&a[i]),cnt[a[i]]++,sum+=a[i];
for(int i = M - 10; i >= 2; i--) cnt[i] += cnt[i+1];
for(int i = M - 10; i >= 2; i--)
{
if(lp[i]!=i) cnt[lp[i]] += cnt[i];
cnt[i/lp[i]] += cnt[i];
}
自己研究研究还是挺快乐的。。。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long lld;
const int maxn = 1000010;
const int M = 10000010;
int a[maxn];
int prime[M];
int lp[M];
lld cnt[M];
int tot;
void init() // precalc in O(n) and get lp[i](the minimum prime factor of i)
{
for(int i = 2; i <= M-10; i++)
{
if(!lp[i]){
lp[i] = i;
prime[tot++] = i;
}
for(int j = 0; j < tot && i*prime[j]<=10000000; j++) {
lp[i*prime[j]] = prime[j];
if(i%prime[j]==0) break;
}
}
}
bool check(lld mid)
{
for(int i = 0;i < tot; i++)
{
lld tmp = mid,sum=0;
while(tmp) {
tmp /= prime[i];
sum += tmp;
}
if(sum < cnt[prime[i]]) return false;
}
return true;
}
int main()
{
init();
int n;
scanf("%d",&n);
lld sum = 0;
for(int i = 0; i < n; i++) scanf("%d",&a[i]),cnt[a[i]]++,sum+=a[i];
for(int i = M - 10; i >= 2; i--) cnt[i] += cnt[i+1];
for(int i = M - 10; i >= 2; i--)
{
if(lp[i]!=i) cnt[lp[i]] += cnt[i];
cnt[i/lp[i]] += cnt[i];
}
lld l = 1 , r = sum , best = -1;
while(l <= r) {
lld mid = l + r >> 1;
if(check(mid)) {
best = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%I64d\n",best);
return 0;
}