2.11~2.12模拟赛

这一次的模拟赛一句话就是……考的不行……

对外宣称是NOIP的模拟赛,实际上难的……(可能因为我是蒟蒻的原因吧……)

一共6道题,55分(WTF?)

以下是题解……

鸥哨

【题解】


不难发现是一个组合数,于是就开始了一条不归路路——不断求阶乘,不断取模……最后妥妥地爆零!!!


为什么呢?——罪魁祸首就是因为一系列的不断取模……于是知道了一个神奇的“防爆”方法——逆元!


然后就可以通过逆元解决组合数爆炸的问题,最后不断更新答案就可以了!!!


什么是逆元?其实我也不清楚,但是并没有关系,只需要知道几件事情:


1、对于a、m,把同余方程中正整数x的最小值记作a模m的逆元


2、逆元实际上就是用乘法代替除法;


3、用扩展欧几里得在算最大公约数的同时算出逆元(即:不定方程ax + my == 1的解x、y);


嗯……大抵上就是这些了,不懂扩展欧几里得的话百度吧……不想写了


【代码】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define L 110000
#define mod 1000000007
#define LL long long
using namespace std;

 inline LL gi() {
  char cj = getchar();
  LL ans = 0, f = 1;
  while (cj < '0' || cj > '9') {
    if (cj == '-') f = -1; cj = getchar();
  }
  while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
  return f * ans;
}

 LL n, t, w, x, y;
LL f[L], inv[L], ans;

 inline LL C (int n, int m) {
  if (n < m) return 0;
  LL res = (f[n] * inv[m]) % mod;
  res = (res * inv[n - m]) % mod;
  return res;
}

 inline LL exgcd(LL a, LL b, LL &x, LL &y) {
  if (b == 0) {
    x = 1, y = 0; return a;
  }
  LL r = exgcd(b, a % b, x, y);
  LL temp = x;
  x = y, y = temp - (a / b) * y;
  return r;
}

 inline LL solve(LL a) {
  LL x, y;
  return exgcd(a, mod, x, y) == 1 ? (x + mod) % mod : -1;
}

 int main() {
  f[0] = 1;
  for (LL i = 1; i <= 100000; ++i) f[i] = (i * f[i - 1]) % mod;
  inv[0] = inv[1] = 1;
  for (LL i = 2; i <= 100000; ++i) inv[i] = solve(i) % mod;
  for (LL i = 2; i <= 100000; ++i) inv[i] = (inv[i] * inv[i - 1]) % mod;
  n = gi(), t = gi(), w = gi();
  for (int i = 1; i <= n; ++i) {
    x = gi(), y = gi();
    LL d = abs(x - w);
    LL num = 0;
    if (t % 2 == 1) {
      if (d % 2 == 0) continue;
      LL pos = (t + 1) / 2 + d / 2;
      num = (ans + C(t, pos - 1)) % mod;
    }
    if (t % 2 == 0){
      if (d % 2 == 1) continue;
      LL pos = t / 2 + 1 + d / 2;
      num = (num + C(t, pos - 1)) % mod;
    }
    num = (num * y) % mod;
    ans = (ans + num) % mod;
  }
  printf("%lld\n", ans % mod);
  return 0;
}

【总结】

这道题考试的时候天真的想着简单的杨辉三角,n^2的算法,最后好像还打萎了,部分分都没有拿全

通过这道题大概了解了一些扩展欧几里得求逆元,总之调了很久,才A的

骨牌移动

【题解】

暴力枚,具体看代码

【代码】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define mod 1000000009
#define L 10010
#define LL long long
using namespace std;

inline int gi() {
  char cj = getchar();
  int ans = 0, f = 1;
  while (cj < '0' || cj > '9') {
    if (cj == '-') f = -1;cj = getchar();
  }
  while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
  return f * ans;
}

int n, m, k, a, b, c, d, x, y;
int p[12][L], ans;
bool vis[12][L], bj;

inline void dfs(int x, int y) {
  if (!vis[x][y]) ans++, vis[x][y] = 1;
  else return ;
  if (y + 2 <= m && p[x][y + 1] == 1) {
    p[x][y] = p[x][y + 1], p[x][y + 1] = p[x][y + 2];
    dfs(x, y + 2);
    p[x][y + 2] = p[x][y + 1], p[x][y + 1] = p[x][y];
  }
  if (y - 2 >= 1 && p[x][y - 1] == 2) {
    p[x][y] = p[x][y - 1], p[x][y - 1] = p[x][y - 2];
    dfs(x, y - 2);
    p[x][y - 2] = p[x][y - 1], p[x][y - 1] = p[x][y];
  }
  if (x + 2 <= n && p[x + 1][y] == 3) {
    p[x][y] = p[x + 1][y], p[x + 1][y] = p[x + 2][y];
    dfs(x + 2, y);
    p[x + 2][y] = p[x + 1][y], p[x + 1][y] = p[x][y];
  }
  if (x - 2 >= 1 && p[x - 1][y] == 4) {
    p[x][y] = p[x - 1][y], p[x - 1][y] = p[x - 2][y];
    dfs(x - 2, y);
    p[x - 2][y] = p[x - 1][y], p[x - 1][y] = p[x][y];
  }
}

int main() {
  freopen("move.in", "r", stdin);
  freopen("move.out", "w", stdout);
  n = gi(), m = gi(), k = gi();
  for (int i = 1; i <= k; ++i) {
    a = gi(), b = gi(), c = gi(), d = gi();
    if (a == c) {
      if (b + 1 == d) p[a][b] = 1, p[c][d] = 2;
      else p[a][b] = 2, p[c][d] = 1;
    }
    if (b == d) {
      if (a + 1 == c) p[a][b] = 3, p[c][d] = 4;
      else p[a][b] = 4, p[c][d] = 3;
    }
  }
  for (x = 1; x <= n; ++x) {
    for (y = 1; y <= m; ++y) 
      if (!p[x][y]) {bj = 1; break;}
    if (bj) break;
  }
  dfs(x, y);
  printf("%d\n", ans - 1);
  return 0;
}

【总结】

考试的时候写别的题去了,并没有写这道题,随便输了一个值,最后果断的爆零了

看了一眼题解后,瞬间后悔考试的时候的斜体顺序,直接用数组记录了每个骨牌的方向,直接爆搜就A了,但是需要注意边界,因为边界WA了一次

围圈

【题解】

一道贪心的题

对于圈中的每一个数i,如果他的l[i]、r[i],满足l[i] > i且r[i] > i,那么一定是移除i,并且保留三个数中的最大数,显然是最优的,于是就会想到对于圈中每一个数进行排序,记录自己左边和右边的数,每一次把min(l[i], r[i]) - i加入ans即可

【代码】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define L 100000 + 1000
#define LL long long
using namespace std;

inline int gi() {
  char cj = getchar();
  int ans = 0, f = 1;
  while (cj < '0' || cj > '9') {
    if (cj == '-') f = -1;cj = getchar();
  }
  while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
  return f * ans;
}

struct node {
  int v, l, r;
}num[L];
LL n, ans, id[L];

inline bool comp (int a, int b) {
  return num[a].v < num[b].v;
}

int main() {
  freopen("game.in", "r", stdin);
  freopen("game.out", "w", stdout);
  n = gi();
  for (int i = 1; i <= n; ++i) id[i] = i, num[i].v = gi(), num[i].l = i - 1, num[i].r = i + 1;
  num[1].l = n, num[n].r = 1;
  sort(id + 1, id + 1 + n, comp);
  for (int i = 1; i < n; ++i) {
    int ind = id[i];
    ans += min(num[num[ind].l].v, num[num[ind].r].v) - num[ind].v;
    num[num[ind].r].l = num[ind].l, num[num[ind].l].r = num[ind].r;
  }
  printf("%lld\n", ans);
  return 0;
}

【总结】

这道题考试的时候本来是接近正解了的,但是可能是排序的方式不对(或者打萎了吧)最后并没有得到预期分数

拿到题解后,简单的看了一下发现了排序的问题,在原代码上做了微调就A了

难解的集合

【题解】

又是一道求逆元的题,在第一中已经用过了扩展欧几里得,所以这道题用一用费马小定理

费马小定理具体的内容很混乱(反正我没怎么懂),但是核心部分很简单:假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p)

对于这道题,不难发现满足组合数,不妨假设f(x)指向p,即最终所有的元素的函数值的函数值等于p

如果已经满足l个f(x)指向了p,那么显然是求从n - 1个数中取l - 1数的组合数,因为数p以确定被取,所以n,l都要减去1,而对于剩下的n - l个数也要在l - 1个数中取,于是将两个方案数相乘加入答案即可,但注意此时的答案是针对一个数的,最后有n个数,所以不要忘记乘以n

【代码】

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define L 101000
#define LL long long
#define mod 1000000007
using namespace std;

inline LL gi() {
  char cj = getchar();
  int ans = 0, f = 1;
  while (cj < '0' || cj > '9') {
    if (cj == '-') f = -1;cj = getchar();
  }
  while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
  return f * ans;
}

LL n, fac[L], ans;

inline LL inv(LL a, LL b) {
  if (a < 0 || b < 0) return 0;
  LL w = 1;
  for (; b; b >>= 1, a = (LL)a * a % mod)
    if (b & 1) w = (LL)w * a % mod;
  return w;
}

int main() {
  freopen("set.in", "r", stdin);
  freopen("set.out", "w", stdout);
  n = gi();
  fac[0] = 1;
  for (LL i = 1; i <= n; ++i) fac[i] = (LL)fac[i - 1] * i % mod;
  for (LL l = 1; l <= n; ++l) {
    LL C1 = (LL)fac[n - 1] * inv(fac[l - 1], mod - 2) % mod * inv(fac[n - l], mod - 2) % mod;
    LL C2 = (LL)inv(l - 1, n - l);
    ans = ans + (LL)C1 * C2 % mod, ans %= mod;
  }
  ans = (LL)ans * n % mod;
  printf("%lld\n", ans);
  return 0;
}

【总结】

对于这道题目,考试的时候并没有想到关于组合数、逆元、费马小定理之类一系列高大上的东西,于是就好不犹豫的打了一个n^n的表,最后20分

后面拿到题解后对于l分情况讨论不是很懂,于是就开始手玩(手玩是一个好东西啊),最后还是搞出来了,不过第一次用lemon测的时候ans忘记乘以n了,WA了一次,不过很快就调了出来

你可能感兴趣的:(考试)