YCU月赛_1 题解

比赛入口

按难度顺序排序!! (毒瘤顺序!! //捂脸)

Bobo的位运算

做法:对于一个n,输出(n & (n - 1)) + (n & (-n)),可以打表找答案,可以发现规律就是输出n,所以只需要快乐的printf出n即可,但是要看清数据范围哦,需用字符串输入n。

代码

#include
int main() {
  char n[101] = {0};
  scanf("%s", n);
  printf("%s\n", n);
}

奇怪的梦

做法:典型的鸡兔同笼问题,暴力枚举出猪的数量和牛的数量。

代码

#include
int main() {
	int m, n, a, b;
	scanf("%d%d%d%d", &m, &n, &a, &b);
	int res1, res2, flag = 0;
	for(res1 = 0; res1 <= n; ++res1) {
      for(res2 = 0; res2 <= n; ++res2) {
        //猪有res1只,牛有res2只,如果符合条件就置标记为1,并退出循环
        if(a*res1 + b*res2 == n && 2*a*res1 + 4*b*res2 == m) { flag = 1; break; }
      }
      if(flag) break; //如果找到直接退出
	}
	//如果是因为找到跳出循环的,就输出答案,不然就输出-1
	if(flag) printf("%d %d\n", res1, res2);
	else printf("-1");
	return 0;
}

Bobo的挑战

做法:很简单,统计每个数字出现的次数,但由于数据可能是负数,所以我向正偏移了100005。

代码

#include
#define N 100005
int a[2*N];
int main() {
  int n, m, mid;
  scanf("%d%d", &n, &m);
  for(int i = 0; i < n; ++i) {
    scanf("%d", &mid);
    a[mid+N]++;
  }
  for(int i = 0; i < m; ++i) {
    scanf("%d", &mid);
    printf("%d\n", a[mid+N]);
  }
  return 0;
}

Lulu的组合数

做法:求出 ∑ i = 0 n C n i ( 其 中 i 为 从 0 到 n 的 奇 数 ) \sum_{i=0}^n C_n^i (其中i为从0到n的奇数) i=0nCni(i0n)做法就是高中组合数公式!!!
C n 1 + C n 3 + C n 5 + ⋯ + C n n ( n 为 奇 数 ) = C n 0 + C n 2 + C n 4 + ⋯ + C n n ( n 为 偶 数 ) = 2 n − 1 C_n^1 + C_n^3 + C_n^5 + \cdots + C_n^n(n为奇数) = C_n^0 + C_n^2 + C_n^4 + \cdots + C_n^n(n为偶数) =2 ^{n-1} Cn1+Cn3+Cn5++Cnn(n)=Cn0+Cn2+Cn4++Cnn(n)=2n1

代码

#include
int main() {
  int n;
  long long res = 1;
  scanf("%d", &n);
  for(int i = 0; i < n - 1; ++i) {
    res *= 2;
  }
  printf("%lld\n", res);
  return 0;
}

超精简代码

#include
int main() {
  int n;
  scanf("%d", &n);
  printf("%lld\n", 1ll << (n - 1));
  return 0;
}

Lulu的线段树

做法:看到有很多学弟问我关于区间种树的问题,这里给泥萌在线送福利…希望弄懂的童鞋做完这题都能感受到一波学姐的温暖。做法就是和那题种树的题目一样啦,先给每棵树一个标记,再对于每个区间遍历,记录下最大最小值,再交换就行,要注意每次初始化最大最小值的原始值哦。

代码

#include
#define N 1003
int a[N];
int main() {
  int n, q;
  scanf("%d%d", &n, &q);
  for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
  int l, r;
  for(int i = 1; i <= q; ++i) {
    scanf("%d%d", &l, &r);
    //初始化最大最小值
    int m = N, mj, M = -1, Mj;
    for(int j = l; j <= r; ++j) {
      if(m > a[j]) m = a[j], mj = j;
      if(M < a[j]) M = a[j], Mj = j;
    }
    //交换两个值
    int t = a[mj]; a[mj] = a[Mj]; a[Mj] = t;
  }
  for(int i = 1; i <= n; ++i) {
    printf("%d%c", a[i], i == n ? '\n' : ' ');
  }
  return 0;
}																								

整理字符串

做法:字符串常规题啦,按照要求更改字符串即可,但是!!这题难点在于!!很多坑!!!有一组数据全是空格!!!

代码

#include
#include
#include
int main() {
  int n;
  scanf("%d", &n);
  getchar();
  for(int i = 0; i < n; ++i) {
    char s[100] = {0};
    gets(s);
    int len = strlen(s), f = 1, pos = 0;
    while(s[pos] == ' ') pos++;  //对前置空格进行处理
    if(islower(s[pos])) f = 0;
    printf("%c", s[pos]);
    for(pos = pos + 1; pos < len; ++pos) {
      //要考虑的有很多哦,还有最后一个为空格的情况
      if(pos != len - 1 && s[pos] == ' ' && isalpha(s[pos+1])) {
        if(f) s[pos+1] = tolower(s[pos+1]);
        else s[pos+1] = toupper(s[pos+1]);
      } else if(s[pos] != ' ') printf("%c", s[pos]);
    }
    puts("");
  }
  return 0;
}

洛洛的圣诞节小铺

做法:要求市场的最大盈利,用结构体对单价从大到小排序。有两种对结构体进行排序的方法,重载运算符和传cmp函数。对于最后不完整的需要特别判断一下,最后要注意精度问题哦。

代码

#include
#include //排序所用头文件
using namespace std;
const int N = 3002;
struct xx {
  int v, w;
  /*bool operator < (const xx &c) const {
    return v * c.w > c.v * w;
  }
  不用写cmp的另一种结构体写法*/
}socks[N];
bool cmp(xx x, xx y) {
  return x.v * y.w > y.v * x.w; //交叉相乘为了避免除法的精度损失
}
int main() {
	int n, d;
	scanf("%d%d", &n, &d);
	for(int i = 0; i < n; ++i) scanf("%d", &socks[i].w);
	for(int i = 0; i < n; ++i) scanf("%d", &socks[i].v);
	sort(socks, socks+n, cmp);
	double sum = 0, res = 0;
	//sum记录了数量,res记录了盈利数
  for(int i = 0; i < n; ++i) {
    //如果遇到边界情况
	  if(sum + socks[i].w >= d) {
	    res += 1.0 * socks[i].v * (d - sum) / socks[i].w;
	    break;
    }
    sum += socks[i].w;
    res += socks[i].v;
	}
	printf("%.1lf", res*10);
	return 0;
}

Wink小学生的分数

做法:要求一个连除等式是否能通过在任意位置加上括号来使它的结果变成整数…一开始没想到该咋写…经过点播后有所领悟,其实就是判断除了第二个数字外其他数字相乘能否整除第二个数,要注意的是数据可能会溢出,需要知道(a * b) % c = ((a % c)*(b % c)) % c,每次取模即可。

代码

#include
int a[10003];
int main() {
  int n, m;
  scanf("%d", &n);
  while(n--) {
    scanf("%d", &m);
    for(int i = 0; i < m; ++i) {
        scanf("%d", &a[i]);
    }
    //特判m == 1的情况
    if(m == 1) puts("Yes");
    else {
      long long res = a[0];
      for(int i = 2; i < m; ++i) {
        //每一轮都要取模防止溢出
        res = res * a[i] % a[1];
        if(res % a[1] == 0)  break;
      }
      if(res % a[1] == 0) puts("Yes");
      else puts("No");
    }
  }
  return 0;
}

送温暖的简单签到题

做法:给定素数a,b,c,d,求1到n中的整数中至少能整除这4个元素中的一个的数有几个?做法有个高大上的名字——容斥原理,实际上掌握高中所学的排列组合就行了。首先得知道1到n中能整除a的数量为n/a,而对于四个素数,如果直接算肯定会有重复的。解决方法就是

整除一个的数量之和-整除任意两个之积的数量之和+整除任意三个之积的数量之和-整除四个之积的数量。

代码

#include
int main() {
  long long a[4], n;
  long long res1 = 0, res2 = 0, res3 = 0, res4 = 0;
  //res1代表整除一个的数量之和
  //res2代表整除任意两个之积的数量之和
  //res3代表整除任意三个之积的数量之和
  //res4代表整除四个之积的数量
  scanf("%lld", &n);
  for(int i = 0; i < 4; ++i) scanf("%lld", a[i]);
  for(int i = 0; i < 4; ++i) {
    res1 += n / a[i];
    for(int j = i + 1; j < 4; ++j) {
      res2 += n / (a[i]*a[j]);
      for(int k = j + 1; k < 4; ++k) {
        res3 += n / (a[i]*a[j]*a[k]);
      }
    }
  }
  res4 = n / (a[0]*a[1]*a[2]*a[3]);
  printf("%lld\n", res1 - res2 + res3 - res4);
  return 0;
}

号码游戏

做法:一个游戏,号码相同的人一起离开,输出最后留下来的一个人或者两个人。
有两种做法:

  1. 用数据结构map暴力计数,最后判断map记录为奇数的元素。
  2. 异或,如果k = 1,根据 a ^ b ^ a = b,直接将所有数字异或即可;如果k = 2,则需找到异或后结果res二进制表示下任意一位的1,为了更简单,我们就取最低位,把这一位标为第m位。这样把数组分成两类,一类为第m位为1,一类为第m位为0。把res异或其中任意一类,即可得到其中一个数,再异或一次res就可得到另外一个数。

暴力代码

#include
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int N = 3e6+6;
unordered_map<int, int> mp;
int main() {
  int n, k;
  read(n); read(k);
  int mid;
  for(int i = 1; i <= n; ++i) {
    read(mid); mp[mid]++;
  }
  int a = 0, b = 0;
  for(auto it : mp) {
  	//是偶数表示已经离开
    if(it.second % 2 == 0) continue; 
    //是奇数就存下来
    if(!a) a = it.first;
    else b = it.first;
  }
  if(k == 2 && a > b) swap(a, b);
  printf("%d ", a);
  if(k == 2) printf("%d\n", b);
  return 0;
}

异或代码

#include
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int N = 3e6+6;
int a[N];
int main() {
  int n, k, res = 0, mid, m;
  read(n); read(k);
  for(int i = 0; i < n; ++i) {
    read(a[i]); res ^= a[i];
  }
  // k = 1时直接输出
  if(k == 1) printf("%d\n", res);
  // k = 2时需要再次异或一下第m位是1的所有数
  else {
    mid = res;
    // lowbit(mid)用于找到最低位的位置
    m = lowbit(mid);
    for(int i = 0; i < n; ++i) {
      if(a[i] & m) mid ^= a[i];
    }
    res ^= mid;
    if(mid > res) swap(mid, res);
    printf("%d %d\n", mid, res);
  }
  return 0;
}

Bobo的树(一)

做法:题意是判断u是否是v的子树,或者是v是否是u的子树,我的做法是用LCA求最近公共祖先,如果u和v的最近公共祖先是其中一个,那么就有包含关系,不然的话就没有包含关系。

代码

#include
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int N = 1e5+5;
//前向星存图
struct xx {
  int next, to;
}edge[N*2];
int n, m, father, deepth, cnt, cur, now;
int head[N], deep[N];
int f[N][20];
void add(int u, int v) {
  edge[++cnt] = xx{ head[u], v };
  head[u] = cnt;
}
//更新祖先数组f,以及深度数组deep
void bfs() {
  queue<int> q;
  while(!q.empty()) q.pop();
  q.push(father); deep[father] = 1;
  while(!q.empty()) {
    cur = q.front(); q.pop();
    for(int i = head[cur]; i; i = edge[i].next) {
      now = edge[i].to;
      if(deep[now]) continue;
      deep[now] = deep[cur] + 1;
      f[now][0] = cur;
      for(int j = 1; j <= deepth; ++j) {
        f[now][j] = f[f[now][j-1]][j-1];
      }
      q.push(now);
    }
  }
}
//求最近公共祖先
int lca(int u, int v) {
  //扩展到同一深度
  if(deep[u] > deep[v]) swap(u, v);
  for(int i = deepth; i >= 0; --i) {
    if(deep[f[v][i]] >= deep[u]) v = f[v][i];
  }
  if(u == v) return u;
  //再同时上升找
  for(int i = deepth; i >= 0; --i) {
    if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
  }
  return f[u][0];
}
int main(){
  read(n); read(father); read(m);
  deepth = (int) (log(n) / log(2)) + 1;
  int u, v;
  for(int i = 1; i < n; ++i) {
    read(u); read(v);
    add(u, v); add(v, u);
  }
  bfs();
  while(m--) {
    read(u); read(v);
    int res = lca(u, v);
    if(res == u) puts("1");
    else if(res == v) puts("2");
    else puts("3");
  }
  return 0;
}

Bobo的树(二)

做法:题意是输出该节点的所有子树权值和,做法用dfs先序遍历该树,在dfs回溯时加上遍历过的权值。

代码

#include
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int N = 1e5+5;
//前向星存图
struct xx {
  int next, to;
}edge[2*N];
int n, rt, q, cnt;
int head[N], f[N];
void add(int u, int v) {
  edge[++cnt] = xx{ head[u], v };
  head[u] = cnt;
}
//先序遍历这颗树
void dfs(int u, int fa) {
  for(int i = head[u]; i; i = edge[i].next) {
    int v = edge[i].to;
    if(v == fa) continue;
    dfs(v, u);
    //回溯时加上权值
    f[u] += f[v];
  }
}
int main(){
  read(n); read(rt); read(q);
  for(int i = 1; i <= n; ++i) read(f[i]);
  int u, v;
  for(int i = 1; i < n; ++i) {
    read(u); read(v);
    add(u, v); add(v, u);
  }
  dfs(rt, 0);
  for(int i = 1; i <= q; ++i) {
    read(rt);
    printf("%d\n", f[rt]);
  }
  return 0;
}

朱朱的斐波那契

做法:求出一个递推公式的第n项值,由于数据很大,要用到矩阵快速幂构造矩阵,下面是构造方法。

  1. 对于首项 F 1 = 1 , F 2 = 1 F_1 = 1, F_2 = 1 F1=1,F2=1
  2. 首先构造出从n = 3开始的初始矩阵: ( F 1 F 2 3 1 ) \begin{pmatrix} F_1 \\ F_2 \\ 3 \\ 1 \end{pmatrix} F1F231
  3. 在通过构造下列矩阵相乘:
    ( F n F n − 1 n + 1 1 ) = ( 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 1 ) ( F n − 1 F n − 2 n 1 ) \begin{pmatrix} F_n \\ F_{n-1} \\ n+1 \\ 1 \end{pmatrix} = \begin{pmatrix} 1&1&1&1 \\ 1&0&0&0 \\ 0&0&1&1 \\ 0&0&0&1 \end{pmatrix} \begin{pmatrix} F_{n-1} \\ F_{n-2} \\ n \\ 1 \end{pmatrix} FnFn1n+11=1100100010101011Fn1Fn2n1
  4. 由于模数太大,乘的过程需要进行快速乘。
  5. 再进行矩阵快速幂即可。

代码

#include
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<LL, LL> pll;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const LL Mod = 1e10+19;
//快速乘模板,解决因模数较大产生的溢出。
LL ksc(LL x, LL y){
  LL L = x * (y >> 25) % Mod * (1 << 25) % Mod;
  LL R = x * (y & ((1 << 25) - 1)) % Mod;
  return (L + R) % Mod;
}
struct Mat {
  LL m[4][4];
  Mat(int f = 0) {
    memset(m, 0, sizeof m);  //默认初始化为零矩阵
    if(f == 1) {
      for(int i = 0; i < 4; ++i) m[i][i] = 1;  //单位矩阵
    } else if(f == 2) {  //构造出的矩阵
      m[0][0] = 1; m[0][1] = 1; m[0][2] = 1; m[0][3] = 1;
      m[1][0] = 1; m[2][2] = 1; m[2][3] = 1; m[3][3] = 1;
    } else if(f == 3) {  // 初始矩阵
      m[0][0] =  1; m[1][0] = 1; m[2][0] = 3; m[3][0] = 1;
    }
  }
  //重载乘号运算符,实现矩阵相乘
  friend Mat operator * (Mat a, Mat b) {
    Mat c;
    for(int k = 0; k < 4; ++k) {
      for(int i = 0; i < 4; ++i) {
        if(!a.m[i][k]) continue;
        for(int j = 0; j < 4; ++j) {
          if(!b.m[k][j]) continue;
          c.m[i][j] = (c.m[i][j] + ksc(a.m[i][k], b.m[k][j])) % Mod;
        }
      }
    }
    return c;
  }
  //重载^运算符用于快速幂
  friend Mat operator ^ (Mat c, LL n) {
    Mat a(2);
    while(n) {
      if(n & 1) c = a * c;
      a = a * a;
      n >>= 1;
    }
    return c;
  }
};
int main() {
  LL n;
  while(~scanf("%lld", &n)) {
    if(n <= 2) puts("1");
    else {
      Mat c(3);
      c = c ^ (n - 2);
      printf("%lld\n", c.m[0][0]);
    }
  }
  return 0;
}

你可能感兴趣的:(YCU月赛_1 题解)