sc2017新高二&高一模拟赛6 总结

时间:2022-12-16 23:08:30

小J的五子棋

题目描述

小J非常热爱玩游戏,尤其喜欢五子棋。
五子棋是一款这样的游戏:
在一个N*N的网格上,玩家依次在格子上放下棋子,如果棋子在同一横排、同一竖排、同一斜线(有两条不同的斜线,从左上到右下以及从右上到左下)连续出现了五个,那么就满足了胜利条件。需要注意的是,与一般的五子棋不同,我们只考虑一个玩家的棋子,即不存在不同的颜色。
小J于是开始了一盘紧张刺激的五子棋。
小J的玩法和一般的玩法不同,小J喜欢先写下一个长度为M的放置棋子序列,然后依次放置棋子,直到满足胜利条件为止,或者说序列上的棋子都放置完了。
那么,小J想知道会在放置第几个棋子之后达到胜利条件。

对于30%的数据,1<=N,M<=10.
对于60%的数据,1<=N,M<=100.
对于100%的数据,1<=N<=20000,M<=200000.


输入格式

第一行两个正整数N,M,意义见题面。
接下来M行,每行两个整数x,y.(x,y)表示放置的棋子的坐标。保证1<=x,y<=N.


输出格式

一行一个整数,表示达到胜利条件时放置的棋子。如果达不到胜利条件,输出-1.


输入样例

5 5
1 1
2 2
3 3
4 4
5 5


输出样例

5


题解(暴力)

人人都会的大水题,空间512M,时间1S,不卡bool卡map。
bool是一个字节,map以后还是用hash代替吧。懒得动手没了20分QAQ。。。


代码

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
#define N 20005

using namespace std;

int n, m;
bool M[N*N];


bool Judge(int x, int y){
    int res = 0;
    for(int i = y; i > 0; i--){
      if(M[(x-1)*n+i])  res ++;
      else  break;
    }
    for(int i = y+1; i <= n; i++){
      if(M[(x-1)*n+i])  res ++;
      else  break;
    }
    if(res >= 5)  return true;

    res = 0;
    for(int i = x; i > 0; i--){
      if(M[(i-1)*n+y])  res ++;
      else  break;
    }
    for(int i = x+1; i <= n; i++){
      if(M[(i-1)*n+y])  res ++;
      else  break;
    }
    if(res >= 5)  return true;

    res = 0;
    for(int i = x, j = y; i > 0 && j > 0; i--, j--){
      if(M[(i-1)*n+j])  res ++;
      else  break;
    }
    for(int i = x+1, j = y+1; i <= n && j <= n; i++, j++){
      if(M[(i-1)*n+j])  res ++;
      else  break;
    }
    if(res >= 5)  return true;


    res = 0;
    for(int i = x, j = y; i > 0 && j <= n; i--, j++){
      if(M[(i-1)*n+j])  res ++;
      else  break;
    }
    for(int i = x+1, j = y-1; i <= n && j > 0; i++, j--){
      if(M[(i-1)*n+j])  res ++;
      else  break;
    }
    if(res >= 5)  return true;
    return false;
}


int main(){
    scanf("%d%d", &n, &m);

    int x, y;
    for(int i = 1; i <= m; i++){
      scanf("%d%d", &x, &y);
      M[(x-1)*n+y] = true;
      if(Judge(x, y)){
        printf("%d\n", i);
        return 0;
      }
    }
    printf("-1\n");
    return 0;
}

小J爱gcd

题目描述

最大公约数,greatest common divisor,简称gcd。
对于非负整数x与y的最大公约数,为最大的正整数z,使得x与y均是z的整数倍。特定地,gcd(0,0)=0,gcd(0,x)=x.
小J非常清楚一点:gcd是数学王国的统治者,一切函数都要臣服于gcd之下,哪怕是增长速度极快的指数函数也不例外。
小J非常想知道当统治者gcd与速度之王结合在一起会是怎么样的效果,于是小J想出了这么一个题目:求解gcd(a^b,c^d).
小J于是将问题交给了你。为了运算方便,将最后的结果对〖10〗^9+7取模后输出。

对于30%的数据,a,b,c,d<=10.
对于60%的数据,a,b,c,d<=10000.
对于100%的数据,a,b,c,d<=10^9.


输入格式

一行四个非负整数a,b,c,d。意义见体面。


输出格式

一行一个整数,为所求的答案


输入样例

2 3 4 1


输出样例

4


题解(Gcd+分解质因数+快速幂)

也很水,先求Gcd,然后分解质因数,然后快速幂完事。还有各种特判。

其实本质就是根据Gcd定义求Gcd罢了。
数据很水,一开始没有分解质因数都能过。。。

吃饭的时候想起了分解质因数要 O(n) 这个梗,笑死我了。。(笑出强大)

有没有人注意到小JJ如此爱党啊


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#define Mod 1000000007

using namespace std;

typedef long long LL;

LL a, b, c, d, ans = 1ll;

LL Pow(LL x, LL n){
    LL res = 1ll;
    while(n){
      if(n & 1)  res = res * x % Mod;
      x = ((x % Mod) * (x % Mod)) % Mod;
      n >>= 1;
    }
    return res;
}

LL Gcd(LL a, LL b){
    return (!b) ? a : Gcd(b, a % b);
}


int main(){
    scanf("%lld%lld%lld%lld", &a, &b, &c, &d);

    if(!a && !c){
      printf("0\n");
      return 0;
    }

    if(!a){
      printf("%lld\n", Pow(c, d));
      return 0;
    }

    if(!c){
      printf("%lld\n", Pow(a, b));
      return 0;
    }

    LL g = Gcd(a, c);

    if(g == 1){
      printf("1\n");
      return 0;
    }

    for(LL i = 2; i * i <= g; i++){
      if(g % i == 0){
        LL cnt1 = 0, cnt2 = 0;

        while(a % i == 0){
          cnt1 ++;
          a /= i;
        }

        while(c % i == 0){
          cnt2 ++;
          c /= i;
        }
        ans = ans * Pow(i, min(cnt1 * b, cnt2 * d)) % Mod;
      }
      while(g % i == 0)  g /= i;
    }

    if(g != 1){
      LL cnt1 = 0, cnt2 = 0;

      while(a % g == 0){
        cnt1 ++;
        a /= g;
      }   

      while(c % g == 0){
        cnt2 ++;
        c /= g;
      }
      ans = ans * Pow(g, min(cnt1 * b, cnt2 * d)) % Mod;
    }

    printf("%lld\n", ans);

    return 0;
}

小J的网红之路

题目描述

小J在网上发现很多人都喜欢打出hhhh……h的句子。
聪明的小J观察一段时间并思考了一番之后发现,这些句子有各种各样的不同含义,哪怕是长度相同的hhhh串都可能会有不同的意思。小J意识到这是一个可以令他闻名网络世界,成为一代网红的好方法。
于是,小J打算给hhhh串编一个翻译系统。
首先,我们需要一本词典。
小J通过分析,得到了一本有m个单词的词典。对于第i个单词,由ai个h组成。注意,不会出现相同数目的h组成的单词。
进一步,我们需要找到一个语法。
小J发现语法总共有e种,每一种语法可以用(a,b,c)表示,表示如果连续三个单词依次为a、b、c,则符合该语法。保证没有相同的语法。
那么,小J实际上就是需要去解读一个长度为n的hhhh串,使得符合尽量多次语法,我们的问题就是要求出符合的语法的最大次数(同一个语法可以在不同位置被符合多次,可以重叠)。如果无法解读,输出-1.

对于30%的数据,n,m,e,ai<=10.
对于60%的数据,n,ai<=1000,m<=50,e<=100.
对于100%的数据,n,m,ai<=1000,e<=100.


输入格式

第一行为三个正整数n,m,e。意义见题面。
接下来m行每行一个正整数ai。意义见题面。
接下来e行,每行三个正整数a,b,c。(a,b,c)为对应的语法。保证1<=a,b,c<=m.


输出格式

一行一个整数,为符合的最大次数。如果无法解读,输出-1.


输入样例

4 1 1
1
1 1 1


输出样例

2


题解(dp)

这题好多人看错题,当成一个sb贪心计数去做,结果集体送命。

本人初次得分10分。

后来别人告诉我正确题意后,我想了想,发现这题的dp也不算难。

先讲正确题意(错误的不讲,太sb了),文章(句子)是由单词组成的,能否解读就是单词能否组成文章,与语法无关。这是-1的情况,用一个背包解决(然而好像不用写背包也能过)。而0是文章能用单词组成但不一定符合任何语法。要注意区分-1与0。

然后就是个 O(ne2) 的dp,记 dp[i][j] 表示当前第i个h,最后用的是第j种语法的答案。转移很简单,再枚举上一个语法,继而算出上一个匹配位置,考虑重叠2个,不重叠,重叠1个的情况就好了。要注意的是中间由很多状态达不到,所以判一下状态合法才转移,这样就不用太多判断边界之类的。

题目还有一个坑点,就是有些地方不用语法匹配也是行的,所以不匹配但合法的前提就是能解读(背包判断),不过我们发现中间空一段不用语法匹配,或者最后不匹配前面匹配等等,相对位置不重要,因为文章都是h组成的,所以是等价的。所以取答案直接最后在n这里取都是可以的(中间的dp状态可以不用)。然后此题就BB完了。

讲个笑话,代码过不了,找别人看了半天,一直改我dp,最后发现dp没错,错误竟然是读入的是编号,没有转成单词长度(这样都有80分。。)。


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define N 1010
#define E 110
#define oo 1e9

using namespace std;

int n, m, e;
int a[N], x[N], y[N], z[N], ans = -1;
int f[N][E];

int main(){

    scanf("%d%d%d", &n, &m, &e);

    for(int i = 1; i <= m; i++)
      scanf("%d", &a[i]);

    for(int i = 1; i <= e; i++){
      scanf("%d%d%d", &x[i], &y[i], &z[i]);
      x[i] = a[x[i]];
      y[i] = a[y[i]];
      z[i] = a[z[i]];
    }

    memset(f, -1, sizeof(f));

    f[0][0] = 0;

    for(int i = 1; i <= m; i++)
     for(int j = a[i]; j <= n; j++)
       f[j][0] = max(f[j][0], f[j-a[i]][0]);

    for(int i = 1; i <= n; i++)  ans = max(ans, f[i][0]);

    for(int i = 1; i <= n; i++)
     for(int j = 1; j <= e; j++){
       if(i < x[j]+y[j]+z[j])  continue;
       for(int k = 0; k <= e; k++){
         if(z[k] == y[j] && y[k] == x[j] && ~f[i-z[j]][k])  f[i][j] = max(f[i][j], f[i-z[j]][k]+1);
         if(z[k] == x[j] && ~f[i-z[j]-y[j]][k])  f[i][j] = max(f[i][j], f[i-z[j]-y[j]][k]+1);
         if(~f[i-z[j]-y[j]-x[j]][k])  f[i][j] = max(f[i][j], f[i-z[j]-y[j]-x[j]][k]+1);
       }
       ans = max(ans, f[i][j]);
    }
    if(f[n][0] == -1)  printf("-1\n");
    else  printf("%d\n", ans);
    return 0;
}

总结

这次三道水题,但依旧考得很差,主要因为该死的map和第三题的题意,对简单dp的掌握也没有想象中那么好。希望运气不要太差。


等到黑夜翻面之后,会是新的白昼。
——《裂缝中的阳光》