Codeforces Round #319 (Div. 2) B C D

11 篇文章 0 订阅
10 篇文章 0 订阅

这场题目挺水的。。感觉半小时能出3题。。可惜没有注册。。

B. Modulo Sum

题意:给100w个数,问是否存在子序列使得整个子序列的和能够整除m。

思路:将所有数对m取模,然后问题就转化成了一个多重背包问题,数比较多需要优化一下。不知道为什么过的人这么少。。

#include <cstdio>
#include <cstring>
int nums[100000], mod[3010], cnt;
bool dp[3010], dp2[3010];
main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 0; i < n; i++) {
        long long a;
        scanf("%I64d", &a);
        mod[a % m]++;
    }
    for(int i = 0; i < m; i++){
        if(mod[i] == 0) continue;
        int total = mod[i];
        long long a = 1;
        while(total >= a){
            nums[cnt++] = (i * a) % m;
            total -= a;
            a <<= 1;
        }
        if(total > 0) nums[cnt++] = (i * total) % m;
    }
    for(int i = 0; i < cnt; i++){
        memset(dp2, 0, sizeof dp2);
        for(int j = 0; j < m; j++){
            dp2[(j + nums[i]) % m] |= dp[j];
        }
        for(int j = 0; j < m; j++)
            dp[j] |= dp2[j];
        dp[nums[i]] = true;
    }
    if(dp[0]) puts("YES");
    else puts("NO");
}
C. Vasya and Petya's Game

题意:两个人玩游戏,V君想一个1~n的整数,P君可以询问V君这个数是否可以整除某个整数数K,询问次数不限,问最少需要询问多少次可以确定这个数。

思路:这题看样例也能猜个差不多。。也很容易就能想到:任意一个大于等于2的自然数都是若干质数的乘积,所以只要询问1~n范围内所有的质数和质数的若干次方能否被这个数整除就可以了。

#include <cstdio>
bool vis[1010], ans[1010];
int prim[1010], cnt;
main() {
    int n;
    scanf("%d", &n);
    for(int i = 2; i <= n; i++){
        if(vis[i] == false){
            prim[cnt++] = i;
            for(int j = i * i; j <= n; j += i) vis[j] = true;
        }
    }
    int total = 0;
    for(int i = 0; i < cnt; i++){
        for(int j = prim[i]; j <= n; j *= prim[i])
            ans[j] = true;
    }
    for(int i = 2; i <= n; i++) if(ans[i]) total++;
    printf("%d\n", total);
    for(int i = 2; i <= n; i++) if(ans[i]) printf("%d ", i);
}
D. Invariance of Tree

题意:给一个序列P1P2...Pn,若一棵树上任意两点u和v有一条边相连,那么这棵树上Pu和Pv也有一条边相连,问这棵树是否存在,如果存在输出任意一种树的n-1条边。

思路:这个题不是很有想法。。虽然想到找环但是没有想清楚。。看了tutorial才明白:

这些数字可以分成若干组,每组都会形成一个互不相交的环(集合), 可以通过i=Pi递归找到每个环。

让所有环和一个”中心环“建边(可以把环想成点),这样就形成了一棵树,也不会再次构成环。

但是在建边的时候边需要错开,这样其他环的点的个数必须是中心环的整数倍(画个图就很容易想明白)。

因为其他所有环都要和中心环相连构成树,所以中心环内的点之间需要建边构成树。

而中心环的点要循环和其他环建边,所以中心环的点的个数要少于3个(大于等于3个的时候就会形成环)。

这样我们就需要做这几件事情:

1.寻找环内点个数为1或2的环作为“中心环”,没有则不能成树;

2.如果中心环点数为1,那么一定可以成树,让这个点和其他各点建边即可;

   如果中心环点数为2,那么其他环的点的个数必须是偶数,否则不能成树,然后中心环两点循环与其他环建边,中心环两点间也要建边。

#include <cstdio>
#include <vector>
using namespace std;

vector<int>link[100020];
bool vis[100020];
int p[100020];
main() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &p[i]);
    int one = 0, two = 0;
    for(int i = 1; i <= n; i++){
        if(vis[i]) continue;
        int v = i;
        while(!vis[v]){
            link[i].push_back(v);
            vis[v] = true;
            v = p[v];
        }
        if(link[i].size() == 1) one = i;
        else if(link[i].size() == 2) two = i;
    }
    if(one){
        puts("YES");
        for(int i = 1; i <= n; i++){
            if(i != one) printf("%d %d\n", one, i);
        }
    }
    else if(two){
        bool flag = true;
        for(int i = 1; i <= n; i++)
            if(link[i].size() % 2)
                flag = false;
        if(!flag) puts("NO");
        else {
            puts("YES");
            printf("%d %d\n", link[two][0], link[two][1]);
            for(int i = 1; i <= n; i++){
                if(two != i){
                    for(int j = 0; j < link[i].size(); j++)
                        printf("%d %d\n", link[two][j & 1], link[i][j]);
                }
            }
        }
    }
    else puts("NO");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值