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

前言

没有前言。


挂盘子

这里写图片描述
这里写图片描述


输入样例

3 10
1 2 3


输出样例

68


题解(组合数学+求逆元+递推)

这题我考试时没做出来,后来看了题解。

这里写图片描述

题解中的 (ji) Cij 的意思,其他都是基础的组合数学,我就不再赘述了。

另外题解中的递推求组合数也可以改成厉害的分块打表(反正我不会),求逆元也可以不用费马小定理,改用Exgcd也行。

需要吐槽的是这题数据错了,搞的我一直过不了,只有40分,其实是标程写错了,rt。。


代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iostream>
#define N 4005
#define M 200005
#define Mod 1000000007

using namespace std;

typedef long long LL;

int n, m, r[N], tot, A[M];
LL f[N], C, ans;

LL Pow(LL x, LL y){
    LL ans = 1LL;
    while(y){
      if(y & 1)  ans = ans * x % Mod;
      x = x * x % Mod;
      y >>= 1;
    }
    return ans;
}

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

    f[0] = 1LL;
    for(int i = 1; i <= n; i++){
      scanf("%d", &r[i]);
      tot += (r[i] << 1);
      f[i] = f[i-1] * i % Mod;
    }

    for(int i = 1; i < n; i++)
     for(int j = i+1; j <= n; j++)
       if(tot-r[i]-r[j] <= m)  A[r[i]+r[j]] += 2;

    for(int i = 2; i < M; i++){
      if(A[i]){
        if(!C){
          C = Pow(f[n], Mod-2LL);
          for(int j = 1; j <= n; j++)  C = C * (m-tot+i+j) % Mod;
        }
        ans = (ans + A[i] * C % Mod * f[n-2] % Mod) % Mod;
      }
      if(C)  C = C * (m-tot+i+n+1) % Mod * Pow(m-tot+i+1, Mod-2LL) % Mod;
    }

    printf("%lld\n", ans);
    return 0;
}

晚会

这里写图片描述
这里写图片描述


输入样例

5
2
0 1
2
1 0
4
1 0 3 2
6
5 2 2 4 5 0
6
3 2 1 0 5 4


输出样例

2
1
2
5
3


题解(找几个环)

这题才是本场比赛的签到题。我一拿到题目先做的是第三题,过了不到20min,就有人第二题有分数了,我立刻意识到第二题肯定很水。其实题目很简单,就是给你一幅图,里面有一些树,一些环,还有环套树之类的,如果是树的话,我们从叶子向上邀请奶牛们就行了,如果发现一个环的话,肯定有一头奶牛要舍弃,于是答案就是 nans ans 就是环的数量。

是不是很水啊,不过20min内提交的那几个人好像都爆10了=。=

时间 O(n2)


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#define MAXN 60

using namespace std;


int R;
int n, ans;
int hate[MAXN];
bool vis[MAXN], used[MAXN];

int main(){
    scanf("%d", &R);
    while(R --){
      scanf("%d", &n);
      for(int i = 0; i < n; i++)
        scanf("%d", &hate[i]);
      memset(used, false, sizeof(used));
      ans = 0;
      for(int i = 0; i < n; i++){
        if(used[i] || hate[i] == i)  continue;
        int j = i;
        do{
          vis[j] = true;
          j = hate[j];
        }while(!vis[j]);
        if(j == i){  
          ans ++;
          for(int k = 0; k < n; k++)
            used[k] |= vis[k];
        }
        j = i;
        do{
          vis[j] = false;
          j = hate[j];
        }while(vis[j]);
      }
      printf("%d\n", n - ans);
    }

    return 0;
}

不得右转

题目描述

Roger和Robot被送到一个星球上。星球的表面可以想象成二维平面。现在给你x数组和y数组,每一组(x[i], y[i])表示一个点。没有三点共线的情况。
Roger会选择{0, 1, …, N – 1}的一个排列,然后按照排列的顺序访问这些点。Roger从一个点到另一个点是走直线段的。他会遵守两个条件:
1.他走的路线不能相交。(如果我们把他走的线段画出来,没有两条线段在非端点的位置相交)
2.从来都不右转。这意味着任意连续三点一定按照逆时针顺序。
你现在的任务是为Roger找一条路线。如果没有这样的路线,请输出0,和一个空行;否则分两行分别输出N和访问的路线(输出点的编号)。


输入格式

多组测试数据。第一行是测试数据组数。
对于每组数据,第一行为N,接下来分别是x数组和y数组(坐标都为整数)。
【限制】
2≤N≤50
-1000 ≤ x[i], y[i] ≤ 1000
点没有重合的


输出格式

对于每组数据输出两行,如题。


输入样例

5
3
-10 0 10
10 -10 10
6
0 0 -3 -3 3 3
-1 1 -3 3 -3 3
10
10 9 8 7 6 5 4 3 2 1
1 4 9 16 25 36 49 64 81 100
8
0 2 -2 4 -4 2 -2 0
1 2 2 4 4 6 6 5
41
-443 887 -16 -388 -245 652 758 640 589 -75 -827 -428 344 -920 -340 -80 -753 546 202 701 -323 804 -603 452 495 -956 -628 417 798 -666 -542 363 11 -558 -466 166 -645 -568 886 902 -933
12 -220 -421 176 -21 -419 -55 8 -12 -278 -7 12 -27 -615 26 -408 -559 -17 -60 -3 79 15 -11 7 -22 -3 83 1 -69 6 -1 485 26 -1 -812 -430 -58 70 403 1 300


输出样例

3
0 1 2
6
0 4 5 3 2 1
10
9 8 7 6 5 4 3 2 1 0
8
4 2 0 1 3 5 6 7
41
27 24 17 8 23 32 14 12 6 19 7 20 11 4 18 28 21 3 0 5 1 39 38 31 40 25 13 34 35 37 26 10 16 2 9 30 29 36 15 33 22

【样例解释】
样例1:
The points form a triangle. Any of the following return values will be accepted: {0,1,2},{1,2,0},{2,0,1}
样例2:
Here is a picture of the points:
这里写图片描述
Here is an example of a different valid solution. This would correspond to a return value of {1,5,3,2,4,0}
这里写图片描述

本题有SPJ。


题解(求几个凸包/叉积/斜率+模拟)

题解后面那堆文字请自行忽略。

我看完这题的题目,计算几何啊啊啊!然而考试结束后我才码出来。
考试时不在状态,码来码去总是心态爆炸,调试也调不出来。

其实这题就是裸的求凸包题。
我的麻烦的方法就是每次从外到内求凸包,然后模拟从外向里走就行了。
没有0的情况。

另外的方法就是用凸包的思想,不过不用每次从外到内做扫描法求凸包,直接将卷包裹法变一下,有人就是这么做的。卷包裹每次要求所有点都在其线同侧,而这题只用左转,所以需要剩下的点在左侧就够了,不断模拟路径,同时这样保证能走完所有的点(共线也要走)。这种方法比上面我的方法要快,而且。。好写多了。。

如何做卷包裹又分为两种,首先找到最边边的点后,再找点,然后使得所有点在其有向连线左边(直接枚举两条边也可以)。一种方法就是叉积找,其实用斜率也可以,直接 O(n) 找最小斜率,跟用叉积判断左右是一样的。

以上的方法殊途同归,本质上都是凸包的变种。

我的码代码的能力还需要急剧提升,不然像计算几何、数据结构这样的码农题,考试时想到但调不出来只能束手无策。


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <cassert>
#define N 60
#define INF 1e9

using namespace std;

int nG, n, m;
struct Point{
    int x, y, id;
    Point() {}
    Point(int _x, int _y):x(_x), y(_y) {}
    bool operator < (const Point& P) const{
        if(x == P.x)  return y < P.y;
        return x < P.x;
    }
    friend Point operator + (Point A, Point B){return Point(A.x + B.x, A.y + B.y);}
    friend Point operator - (Point A, Point B){return Point(A.x - B.x, A.y - B.y);}
};

int Det(Point A, Point B){return A.x * B.y - A.y * B.x;}
bool used[N];

Point p[N], Con[N][N];
int Cnt[N], s[N];


void Get_Convex(int id){
    sort(p, p+n);

    int res = 0;
    for(int i = 0; i < n; i++)
      if(p[i].x == INF && p[i].y == INF)  res ++;

    n -= res;
    Cnt[id] = 0;
    for(int i = 0; i < n; i++){
      while(Cnt[id] > 1 && Det(p[i] - Con[id][Cnt[id]], Con[id][Cnt[id]] - Con[id][Cnt[id]-1]) > 0)  Cnt[id] --;
      Con[id][++Cnt[id]] = p[i];
    }
    int k = Cnt[id];
    for(int i = n-2; i >= 0; i--){
      while(Cnt[id] > k && Det(p[i] - Con[id][Cnt[id]], Con[id][Cnt[id]] - Con[id][Cnt[id]-1]) > 0)  Cnt[id] --;
      Con[id][++Cnt[id]] = p[i];
    }
    if(n != 1)  Cnt[id] --;
    for(int i = 1; i <= Cnt[id]; i++)
      used[Con[id][i].id] = true;

    for(int i = 0; i < n; i++)
      if(used[p[i].id])  p[i].x = p[i].y = INF;
}

bool Check(){
    bool ok = true;
    for(int i = 0; i < m && ok; i++)  if(!used[i])  ok = false;
    return ok;
}

int main(){

    scanf("%d", &nG);

    while(nG --){
      scanf("%d", &n);
      m = n;
      for(int i = 0; i < n; i++) 
        scanf("%d", &p[i].x);
      for(int i = 0; i < n; i++)
        scanf("%d", &p[i].y);
      for(int i = 0; i < n; i++)
        p[i].id = i;

      memset(Cnt, 0, sizeof(Cnt));
      memset(used, false, sizeof(used));

      int id;
      for(id = 1; ;id ++){
        if(Check())  break;
        Get_Convex(id);
      }
      id --;

      s[1] = 1;
      for(int i = 1; i < id; i++){
        bool f = false;
        for(int j = 1; j <= Cnt[i+1] && !f; j++){
          bool ok = true;
          Point A = Con[i][(s[i]-1)?s[i]-1:Cnt[i]], B = Con[i+1][j];
          for(int u = 1; u <= Cnt[i+1]; u++)
            if(Det(B - A, Con[i+1][u] - A) < 0){
              ok = false;
              break;
            }
          if(ok){
            s[i+1] = j;
            f = true;
          }
        }
      }
      printf("%d\n", m);

      for(int i = 1; i <= id; i++){
        printf("%d ", Con[i][s[i]].id);
        for(int j = s[i]+1; j <= Cnt[i]; j++)
          printf("%d ", Con[i][j].id);
        for(int j = 1; j < s[i]; j++)
          printf("%d ", Con[i][j].id);
      }
      printf("\n");
    }
    return 0;
}

总结

这场比赛考得很糟糕,第三题有人A掉我却调不出来,第一题也没有去水分。希望重要的考试的时候不要犯一些不该犯的错误,不要留下什么遗憾。


这里写图片描述

此生无悔入四月,来世愿做友人A。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值