2.11~2.12模拟赛

来源:互联网 发布:拍动态图片软件 编辑:程序博客网 时间:2024/06/16 10:58

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

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

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

以下是题解……

鸥哨

【题解】


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


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


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


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


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


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


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


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


【代码】

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>#include <stack>#include <map>#include <vector>#include <queue>#define L 110000#define mod 1000000007#define LL long longusing 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 <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>#include <stack>#include <map>#include <vector>#include <queue>#define mod 1000000009#define L 10010#define LL long longusing 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 <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>#include <stack>#include <map>#include <vector>#include <queue>#define L 100000 + 1000#define LL long longusing 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 <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>#include <stack>#include <map>#include <vector>#include <queue>#define L 101000#define LL long long#define mod 1000000007using 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了一次,不过很快就调了出来

0 0
原创粉丝点击