ecnu - 计软联谊 - 游族杯 - 所有因子预处理

12 篇文章 0 订阅

Time limit per test: 7.0 seconds
Time limit all tests: 7.0 seconds
Memory limit: 512 megabytes

Problem Description

在计算机和软件专业的联谊会上,计算机和软件的同学相间着排成一列。现在要计算相邻两个同学的友谊度。
友谊度 friend(a,b)是这么计算的:令 a, b两个整数分别是两个同学的属性,两个同学的友谊度取决于a,b第k大的公约数。如果不存在,就说明这两个同学之间完全没有友谊,友谊度为 −1。

Input

第一行是数据组数 T(1≤T≤60)。
对于每组数据:
第一行:首先是学生的数量n(1≤n≤10^5),约定的常数k(1≤k≤10^6)。第二行:n个整数,依次表示这些学生的属性值:m1,m2,…,mn(1≤mi≤10^6)。

Output

对于每组数据输出一行,以 Case x: 开头(x 表示数据编号,从1开始),后面是n−1个整数,分别是friend(m1,m2),friend(m2,m3),…,friend(mn−1,mn),整数和整数之间用空格隔开。

Examples

Input
2
3 1
4 6 12
6 2
13 12 12 24 36 30
Output
Case 1: 2 6
Case 2: -1 6 6 6 3

题解

可以考虑预处理出1到10^6中所有的数的所有因数。给每个数开个vector然后用类似筛法的方法,从小到大枚举,然后给所有这个数的倍数的vector加上这个数,结果我们就可以在因数总个数的线性时间内获得所有因数。如果你有统计过的话,是 10^7多一点。然后我们对每两个数求出 gcd,由于第 k大的 gcd 一定是 gcd 的因数,所以我们只要求 gcd 第 k小的因数然后除以它就可以了。如果不存在就是 −1。
此题还可以通过离线处理加速(因为可能取到的 k值是有限的),通过欧拉筛加速。但对于本题来说时限够宽以至于不做优化也能 AC。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<algorithm>
const int maxn = 1e6 + 10;
using namespace std;

int pr[maxn], num;
int a[maxn];
vector<int> G[maxn];

void init() {
    for(int n = 1; n <= 1e6 ; n++) {
        for(int i = 1; i *i <= n; i++) {
            if(n % i) continue;
            G[n].push_back(i);
            if(n / i != i) G[n].push_back(n / i);
        }
        sort(G[n].begin(), G[n].end());
    }
}

int divv(int n, int k) {
    num = G[n].size();
    if(k > G[n].size()) return -1;
    return G[n][num - k];
}

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

int main() {
    int T, k, n;
    init();
    scanf("%d", &T);
    for(int cse=1;cse<=T;cse++){
        scanf("%d %d", &n, &k);
        for(int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
        }
        printf("Case %d:", cse);
        for(int i = 1; i < n; i++) {
            int g = gcd(a[i], a[i - 1]);
            printf(" %d", divv(g, k));
        }
        printf("\n");
    }
    return 0;
}

练习网址:http://acm.ecnu.edu.cn/contest/16/problem/K/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值