Codeforces Round #383 (Div. 2)

Codeforces Round #383 (Div. 2)#

A. Arpa’s hard exam and Mehrdad’s naive cheat##

题意:
求1378n的末位数字(0<=n<=109)。

分析:
小学数学题。时间复杂度为O(1)。

代码如下:

#include <cstdio>
#include <cstring>

int main() {
  //freopen("input.txt", "r", stdin);
  int n;
  while (~scanf("%d", &n)) {
    if (n == 0) printf("1\n");
    else {
      int buf = n % 4;
      switch (buf) {
      case 0: puts("6"); break;
      case 1: puts("8"); break;
      case 2: puts("4"); break;
      case 3: puts("2"); break;
      }
    }
  }
  return 0;
}

B. Arpa’s obvious problem and Mehrdad’s terrible solution##

题意:
定义一种运算,实际上就为位运算中的按位异或。给出n(1<=n<=105)个数(1<=ai<=105),给出x(0<=x<=10^5)。求有多少对ai和aj按位异或等于x。

分析:
由于ai的范围不大,可用数组记录ai出现的次数,对每个ai,ai^x即等于要找的aj,再统计求和即可。注意x=0的情况,因为没考虑到这点虽比赛中过了,但比赛结束后system test被刷下来了。时间复杂度为O(10^5)。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;

typedef long long ll;

const int maxn = 100000 + 3;
int cnt[maxn], n, x;

inline void read(int& buf) {
  buf = 0;
  char c = getchar();
  while (!isdigit(c)) c = getchar();
  while (isdigit(c)) {
    buf = (buf << 3) + (buf << 1) + (c - '0'); c = getchar();
  }
}

int main() {
  //freopen("input.txt", "r", stdin);
  while (~scanf("%d", &n)) {
    scanf("%d", &x);
    memset(cnt, 0, sizeof(cnt));
    int min_val = 0x7fffffff, max_val = 0;
    for (int i = 1; i <= n; i++) {
      int buf; read(buf);
      cnt[buf] += 1;
      min_val = min(min_val, buf);
      max_val = max(max_val, buf);
    }
    ll ans = 0LL;
    for (int i = min_val; i <= max_val; i++) {
      if (!cnt[i]) continue;
      int need = x ^ i;
      if (need < min_val || need > max_val) continue;
      if (cnt[need]) {
	if (!x) ans += ll(cnt[i]) * ll(cnt[need] - 1);
	else ans += ll(cnt[i]) * ll(cnt[need]);
      }
    }
    printf("%I64d\n", ll(ans >> 1));
  }
  return 0;
}

C. Arpa's loud Owf and Mehrdad's evil plan##

题意:
有n(1<=n<=100)个人,编号1到n,每个人有一个数字crush_i(1<=crush_i<=n),有一个游戏规则如下,假设从编号为x的人开始一轮游戏,他对着编号为crush_x的人喊:“Owww...wwf。”(w重复t次),如果t大于1的话,编号为crush_x的人对着编号为crush_crush_x的人喊:“Owwww...wwwf。”(w重复t-1次)。这样一直持续下去,直到有个人收到“Owf”(w只重复1次),那么这个人就是这一轮游戏的Joon-Joon。要求解最小的t(t大于等于1),使得对于每个人x,他的Joon-Joon为y的话,那么y的Joon-Joon也为x。如果找不到这样的t,就输出-1。注意:可以自己朝自己喊话。

分析:
将每个人作为一个节点,x朝y喊话的话,就连一条从x到y的有向边,若存在点的入度为0则无解,输出-1,有解的话则表示图由若干个简单环构成,找出每个简单环的长度,并准备一个备选长度的集合,若这个长度为奇数,则直接添入备选集合,若这个长度为偶数,则除以2(题目要求最小的t,且除以2后也满足条件,则相对原数更优)后添入备选集合,最终的答案就是备选集合中所有数的最小公倍数。
时间复杂度为O(nlogn)。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 100 + 3;
int G[maxn];
int in_degree[maxn], n;
vector<int> len;
bool already[maxn];

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

int lcm(int a, int b) {
  return a * b / gcd(a, b);
}

void dfs(int st, int now, int step) {
  already[now] = true;
  if (now == st) (step & 1) ? len.push_back(step) : len.push_back(step >> 1);
  else dfs(st, G[now], step + 1);
}

int solve() {
  len.clear();
  memset(already, 0, sizeof(already));
  for (int i = 1; i <= n; i++)
    if (!already[i]) {
      already[i] = true;
      dfs(i, G[i], 1);
    }
  if (len.size() == 1) return len[0];
  int buf = lcm(len[0], len[1]);
  for (int i = 2; i < len.size(); i++) buf = lcm(buf, len[i]);
  return buf;
}

int main() {
  //freopen("input.txt", "r", stdin);
  while (~scanf("%d", &n)) {
    for (int i = 1; i <= n; i++) {
      G[i] = -1; in_degree[i] = 0;
    }
    for (int i = 1; i <= n; i++) {
      int to; scanf("%d", &G[i]);
      in_degree[G[i]] += 1;
    }
    bool ok = true;
    for (int i = 1; i <= n; i++)
      if (!in_degree[i]) { ok = false; break; }
    ok ? printf("%d\n", solve()) : puts("-1");
  }
  return 0;
}

D. Arpa's weak amphitheater and Mehrdad's valuable Hoses##

题意:
一共有n(1<=n<=1000)个hos,每个hos有对应的重量(1<=wi<=1000)和颜值(1<=bi<=10^6),其中有m(0<=m<=min(n*(n-1)/2, 10^5))对朋友,朋友关系具有传递性,现打算邀请其中的一些hos来参加party,对于同一只友谊的小船上的hos,如果打算邀请他们的话,要么只邀请一个hos,要么这艘船上所有的hos都要邀请,但是party上的重量总和w(1<=w<=1000)有限,保证邀请的hos的重量之和不超过w的前提下,使得他们的颜值的总和尽量大。求这个最优的颜值总和的数值。

分析:
连通块+背包dp。每个hos看作一个节点,朋友关系表示连以边,构图,找出每个连通块的重量总和和颜值总和作为物品,再将每个hos也作为一个物品,但要么加入连通块物品,不再加入连通块中的hos,要么不加入连通块物品,加入连通块中的其中一个hos,要么两者都不加入,即变成有三种选择的01-背包,时间复杂度为O(n*w)。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;

typedef long long ll;
const int maxn = 1000 + 3;
int n, m, w_bound, w[maxn], b[maxn], color[maxn];
vector<int> G[maxn];
vector<int> dp_b, dp_w;
int dp[maxn], group_size[maxn], group_cnt;

inline void read(int& buf) {
  buf = 0;
  char c = getchar();
  while (!isdigit(c))  c = getchar();
  while (isdigit(c)) {
    buf = (buf << 3) + (buf << 1) + (c - '0');
    c = getchar();
  }
}

int sum_bi, sum_wi;

void dfs(int idx, int to_be) {
  color[idx] = to_be;
  sum_bi += b[idx];
  sum_wi += w[idx];
  group_size[to_be] += 1;
  dp_b.push_back(b[idx]);
  dp_w.push_back(w[idx]);
  int bound = G[idx].size();
  for (int i = 0; i < bound; i++) {
    int to = G[idx][i];
    if (!color[to]) dfs(to, to_be);
  }
}

void pre_dp() {
  dp_b.clear(); dp_w.clear();
  memset(color, 0, sizeof(color));
  memset(group_size, 0, sizeof(group_size));
  group_cnt = 0;
  for (int i = 1; i <= n; i++) {
    if (!color[i]) {
      sum_bi = sum_wi = 0;
      dfs(i, ++group_cnt);
      dp_w.push_back(sum_wi); dp_b.push_back(sum_bi);
      group_size[group_cnt] += 1;
    }
  }
  //int bound = dp_w.size();
  //for (int i = 0; i < bound; i++) {
  //  printf("%d %d\n", dp_w[i], dp_b[i]);
  //}
}

void solve() {
  //printf("group_cnt is %d\n", group_cnt);
  for (int i = 0; i <= w_bound; i++) dp[i] = 0;
  int idx = 0;
  for (int i = 1; i <= group_cnt; i++) {
    for (int j = w_bound; j >= 0; j--)
      for (int k = 0; k < group_size[i]; k++)
	if (j >= dp_w[idx + k])
	  dp[j] = max(dp[j], dp[j - dp_w[idx + k]] + dp_b[idx + k]);
    idx += group_size[i];
    //printf("group_size is %d\n", group_size[i]);
  }
}

int main() {
  //freopen("input.txt", "r", stdin);
  while (~scanf("%d", &n)) {
    read(m); read(w_bound);
    for (int i = 1; i <= n; i++) G[i].clear();
    for (int i = 1; i <= n; i++) read(w[i]);
    for (int i = 1; i <= n; i++) read(b[i]);
    while (m--) {
      int x, y;
      read(x); read(y);
      G[x].push_back(y); G[y].push_back(x);
    }
    pre_dp(); solve();
    printf("%d\n", dp[w_bound]);
  }
  return 0;
}
posted @ 2016-12-07 16:34  Alexis Lacour  阅读(233)  评论(0编辑  收藏  举报