Codeforces Round #470 (Div. 1)

Contests 链接:Codeforces Round #470 (Div. 1)
过题数:3
排名:315/1183

A. Primal Sport

题意

Alice A l i c e Bob B o b 两人在玩一个游戏, Alice A l i c e 先开始,两人轮流进行,第 i i 次游戏中,对于一个整数 Xi,选择一个素数 p<Xi p < X i ,然后找到一个最小的整数 Y Y ,使得 YX Y Y 能被 p 整除,将这个数字作为下一次游戏的新值 Xi+1 X i + 1 继续进行游戏,现在游戏已经进行了两次,得到了 X2 X 2 的值,问最小的可能的 X0 (X03) X 0   ( X 0 ≥ 3 ) 的值为多少。

输入

输入为一个整数 X2 (4X2106) X 2   ( 4 ≤ X 2 ≤ 10 6 ) ,数据保证 X2 X 2 是合数。

输出

输出最小的可能的 X0 X 0 的值。

样例

输入
14
输出
6
提示
X0=6 X 0 = 6 ,则其中一种合法的游戏过程如下:
1. Alice A l i c e 选择 p=5 p = 5 X1 X 1 的值为 10 10
2. Bob B o b 选择 p=7 p = 7 X2 X 2 的值为 14 14
输入
20
输出
15
提示
X0=15 X 0 = 15 ,则其中一种合法的游戏过程如下:
1. Alice A l i c e 选择 p=2 p = 2 X1 X 1 的值为 16 16
2. Bob B o b 选择 p=5 p = 5 X2 X 2 的值为 20 20
输入
8192
输出
8191
题解

我们可以根据 Xi X i 的值确定 Xi1 X i − 1 的合法的取值区间: [Xip+1,Xi] [ X i − p + 1 , X i ] ,其中 p p Xi 的最大的质因子,对于 Xi1Xip+1 X i − 1 ≥ X i − p + 1 的值,只要选择质数 p p 就可以通过 Xi1 得到 Xi X i 的值,对于 Xi1<Xip+1 X i − 1 < X i − p + 1 的值,如果选择质数 p p ,则只能得到 Xip 的值,如果选择其他质数,由于 p p 是最大的质因数,选择其他质数能够改变的增量一定小于 p,所以 Xi<Xi X i ′ < X i 。但是最小的 Xi1 X i − 1 并不能得到最小的 Xi2 X i − 2 ,因此对于 X2 X 2 我们需要枚举所有可能的 X1 X 1 的值,来找到最小的 X0 X 0 的值。

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;

#define LL long long
const int maxn = 1000000 + 100;
int cnt, n;
bool vis[maxn];
int prime[maxn];

void Prime() {
    for(int i = 2; i < maxn; ++i) {
        if(!vis[i]) {
            prime[cnt++] = i;
        }
        for(int j = 0; j < cnt && i < maxn / prime[j]; ++j) {
            int k = i * prime[j];
            vis[k] = true;
            if(i % prime[j] == 0) {
                break;
            }
        }
    }
}

int Find(int x) {
    int ret;
    int xx = x;
    for(int i = 0; i < cnt && prime[i] <= xx / prime[i]; ++i) {
        while(x % prime[i] == 0) {
            x /= prime[i];
            ret = prime[i];
        }
    }
    if(x != 1) {
        ret = x;
    }
    return ret;
}

int main() {
    #ifdef LOCAL
        freopen("test.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    #endif // LOCAL
    ios::sync_with_stdio(false);

    Prime();
    while(scanf("%d", &n) != EOF) {
        int Start = Find(n);
        int ans = INT_MAX;
        for(int i = n - Start + 1; i <= n; ++i) {
            int tmp = i - Find(i) + 1;
            if(tmp >= 3) {
                ans = min(ans, tmp);
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}

B. Producing Snow

题意

Bob B o b 从第 1 1 天到第 n 天每天堆体积为 Vi V i 体积的雪人,第 i i 天的时候每一个已经堆好的雪人会融化 Ti 的体积,问在第 i i 天当天所有已经堆好的且没有完全融化的雪人会融化的总的体积。

输入

第一行包含一个整数 n (1n105),第 2 2 行包含 n 个整数 V1,V2,,Vn (0Vi109) V 1 , V 2 , ⋯ , V n   ( 0 ≤ V i ≤ 10 9 ) ,第 3 3 行包含 n 个整数 T1,T2,,Tn (0Ti109) T 1 , T 2 , ⋯ , T n   ( 0 ≤ T i ≤ 10 9 )

输出

输出 n n 个整数,第 i 个整数表示第 i i 天融化的雪人体积。

样例

输入
3
10 10 5
5 7 2
输出
5 12 4
提示
第一天堆了一个体积为 10 的雪人,融化了 5 5 的体积,剩下一个体积为 5 的雪人,第二天堆了一个体积为 10 10 的雪人,每个雪人都融化 7 7 的体积,第一天的 5 体积的雪人全都融化了,第二天的雪人融化了 7 7 体积,总共融化了 12 体积,第二天只剩下一个体积为 3 3 的雪人,第三天堆了一个体积为 5 的雪人,第二天雪人剩下的体积和第三天雪人的体积都大于等于第三天融化的体积,第三天总共融化了 4 4 体积的雪人。
输入
5
30 25 20 15 10
9 10 12 4 13
输出
9 20 35 11 25

题解

假设第 1 天堆起来的雪人体积为无穷大,那么到第 i i 天的时候这个雪人融化的体积就为 j=1iTj,第 i i 天刚堆起来的雪人没有受到前 i1 天融化的影响,但是可以假设这个雪人是从第 1 1 天堆起来的,第 i 天的时候融化了 i1j1Ti ∑ j − 1 i − 1 T i 的体积,导致它第 i i 天的体积为 Vi,因此可以认为这个雪人在第 1 1 天堆起来的时候的体积为 Vi+j=1i1Tj,我们将到第 i i 天的所有雪人的体积都转化为 Vi=Vi+j=1i1Tj 后放入 map m a p 中,到第 i i 天完全融化的雪人就是 Vkj=1iTj 的雪人,将这些雪人在第 i i 天融化的体积(Vkj=1i1Tj)计算出来,然后从 map m a p 中删去它们,再计算第 i i 天能融化体积 Ti 的雪人的个数 cnt c n t (可以 O(1) O ( 1 ) 维护),于是第 i i 天融化的雪人体积就为 (Vjk=1i1Tk)+Ti×cnt,其中 i1k=1Tk<Vjik=1Tk ∑ k = 1 i − 1 T k < V j ′ ≤ ∑ k = 1 i T k

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;

#define LL long long
const int maxn = 100000 + 100;
int n, cnt;
LL mp_cnt;
map<LL, int> mp;
map<LL, int>::iterator it, itt;
LL v[maxn], t[maxn];
map<LL, int>::iterator del[maxn];

int main() {
    #ifdef LOCAL
        freopen("test.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    #endif // LOCAL
    ios::sync_with_stdio(false);

    while(scanf("%d", &n) != EOF) {
        mp_cnt = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%I64d", &v[i]);
        }
        for(int i = 1; i <= n; ++i) {
            scanf("%I64d", &t[i]);
            t[i] += t[i - 1];
        }
        mp.clear();
        LL ans;
        for(int i = 1; i <= n; ++i) {
            ans = 0;
            cnt = 0;
            v[i] += t[i - 1];
            ++mp[v[i]];
            ++mp_cnt;
            it = mp.upper_bound(t[i]);
            for(itt = mp.begin(); itt != it; ++itt) {
                ans += ((itt->first) - t[i - 1]) * (itt->second);
                del[cnt++] = itt;
                mp_cnt -= itt->second;
            }
            for(int j = 0; j < cnt; ++j) {
                mp.erase(del[j]);
            }
            ans += mp_cnt * (t[i] - t[i - 1]);
            if(i != 1) {
                printf(" ");
            }
            printf("%I64d", ans);
        }
        printf("\n");
    }

    return 0;
}

C. Perfect Security

题意

有两个长度为 n n 的序列 a1,a2,,an b1,b2,,bn b 1 , b 2 , ⋯ , b n ,对于每一个 ai a i ,在 b b 序列中找到一个数字与其异或,成为新序列的第 i 个元素 ci c i ,然后从 b b 序列中删去被选中的数字,求能够构成的新序列中字典序最小的序列。

输入

第一行包含一个整数 n (1N3×105),第二行为 n n 个整数 a1,a2,,an (0ai<230),第三行为 n n 个整数 b1,b2,,bn (0bi<230)

输出

输出 n n 个整数,第 i 个整数表示所求新序列的第 i i 项。

样例

输入
3
8 4 13
17 2 7
输出
10 3 28
提示
82=10 47=3 4 ⨁ 7 = 3 1317=28 13 ⨁ 17 = 28 。对于其他可能的答案: (25,6,10),(25,3,15),(10,21,10),(15,21,15),(15,6,28) ( 25 , 6 , 10 ) , ( 25 , 3 , 15 ) , ( 10 , 21 , 10 ) , ( 15 , 21 , 15 ) , ( 15 , 6 , 28 ) ,都比 (10,3,28) ( 10 , 3 , 28 ) 的字典序大。

输入
5
12 7 87 22 11
18 39 9 12 16
输出
0 14 69 6 44
输入
10
331415699 278745619 998190004 423175621 42983144 166555524 843586353 802130100 337889448 685310951
226011312 266003835 342809544 504667531 529814910 684873393 817026985 844010788 993949858 1031395667
输出
128965467 243912600 4281110 112029883 223689619 76924724 429589 119397893 613490433 362863284
题解

将所有 b b 序列中的数字建一棵字典树,从 1 n n 对于每一个 i,都取一个 b b 序列中与 ai 异或值最小的,然后将这个数字在字典树中出现的次数 1 − 1

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;

#define LL long long
const int maxm = 300000 + 100;
const int maxn = 300000 * 32 + 100;
const int Size = 2;
struct Trie {
    int root, cnt;
    int val[maxn], tree[maxn][Size];

    int creat() {
        ++cnt;
        memset(tree[cnt], 0, sizeof(tree[cnt]));
        val[cnt] = 0;
        return cnt;
    }

    void Init() {
        cnt = 0;
        root = creat();
    }

    int id(int x, int dig) {
        return ((x >> (31 - dig)) & 1);
    }

    void add(int x) {
        int pos = root;
        for(int i = 0; i < 32; ++i) {
            int w = id(x, i);
            if(tree[pos][w] == 0) {
                tree[pos][w] = creat();
            }
            pos = tree[pos][w];
            ++val[pos];
        }
    }

    int query(int x) {
        int ret = 0;
        int pos = root;
        for(int i = 0; i < 32; ++i) {
            int w = id(x, i);
            if(tree[pos][w] == 0 || val[tree[pos][w]] == 0) {
                w = !w;
                ret |= (1 << (31 - i));
            }
            pos = tree[pos][w];
            --val[pos];
        }
        return ret;
    }
};
Trie t;
int n, x;
int num[maxn];

int main() {
    #ifdef LOCAL
        freopen("test.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    #endif // LOCAL
    ios::sync_with_stdio(false);

    while(scanf("%d", &n) != EOF) {
        t.Init();
        for(int i = 0; i < n; ++i) {
            scanf("%d", &num[i]);
        }
        for(int i = 0; i < n; ++i) {
            scanf("%d", &x);
            t.add(x);
        }
        for(int i = 0; i < n; ++i) {
            if(i != 0) {
                printf(" ");
            }
            printf("%d", t.query(num[i]));
        }
        printf("\n");
    }

    return 0;
}
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值