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

来源:互联网 发布:淘宝大学开网店 编辑:程序博客网 时间:2024/06/05 17:22

这场题目挺水的。。感觉半小时能出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");}


0 0
原创粉丝点击