2020.02.22寒假集训考试

序言:

​ 寒假就要结束了,马上就要上网课了,剩下的时间就要复习和预习大一下的知识了,博客可能暂时停更了,谢谢大家的观看。武汉加油!中国加油!

​ 今天是寒假集训的最后一次考试了,题很不错,思维题较多,涉及算法的并不多,感谢jwGG以及实验室的学长学姐们亲情出题,谢谢cy老师和jwGG的假期培训。

​ 北大的dalao屠榜,,差距很大呀,我们仍需努力。
2020.02.22寒假集训考试_第1张图片

题解:

A:熊熊对对碰

map计数器的应用,因为他要计算相反数和他本身的和,所以0的时候要考虑。

AC代码:

#include 
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int n;
map<pair<int, int>, int> a;
pair<int, int> q;
int main()
{
    scanf("%d", &n);
    while (n--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        if (a.count({-1 * x, -1 * y}) != 0) //如果它的相反数存在,就直接计算进去
        {
            a[{-1 * x, -1 * y}]++;
        }
        else //若他的相反是不存在,就计算它本身
            a[{x, y}]++;
    }
    int cnt = 0;
    for (map<pair<int, int>, int>::iterator i = a.begin(); i != a.end(); ++i)
    {
        if ((*i).second % 2 == 0)
            cnt += (*i).second;
    }
    printf("%d\n", cnt);
    return 0;
}

B:秘籍

前缀和+快慢指针尺取法的应用,暴力也可以。

AC代码:

#include 
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 300002;
int n, k;
int a[maxn];
bool flag = 0;
int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
    {
        int x;
        scanf("%d", &x);
        a[i] = a[i - 1] + x; //前缀和
    }
    int j = 1;
    for (int i = 0; i <= n && j <= n;) //快慢指针
    {
        if (a[j] - a[i] < k)
            j++;
        else if (a[j] - a[i] > k)
            i++;
        else
        {
            printf("%d %d", i + 1, j);
            flag = 1;
            break;
        }
    }
    if (flag == 0)
        puts("tiangeniupi");
    return 0;
}

C:jwGG的签到题

数学公式的推导+审题,一开始没审清题WA了两发。因为数据范围的情况,要开unsigned long long。

2020.02.22寒假集训考试_第2张图片

AC代码:

#include 
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int main()
{
    int t;
    ll a[20] = {0, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, 9999999999, 99999999999, 999999999999, 9999999999999, 99999999999999, 999999999999999, 9999999999999999, 99999999999999999, 999999999999999999};
    scanf("%d", &t);
    while (t--)
    {
        unsigned long long cnt = 0;
        unsigned long long x, y;
        scanf("%llu%llu", &x, &y);
        for (int i = 1; i <= 18; i++)
        {
            if (a[i] <= y)
                ++cnt;
            if (a[i] > y)
                break;
        }
        cnt *= x;
        printf("%llu\n", cnt);
    }
    return 0;
}

D:jwMM的疯狂A-B

最简单的签到题,set的应用。

AC代码:

#include 
using namespace std;
int n, m;
int x;
set<int> a, b;
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &x);
        a.insert(x);
    }
    for (int i = 1; i <= m; i++)
    {
        scanf("%d", &x);
        b.insert(x);
    }
    int cnt = 0, flag = 0;
    for (set<int>::iterator it = a.begin(); it != a.end(); it++)
    {
        if (!b.count(*it))
        {
            printf("%d\n", *it);
            cnt++;
            flag = 1;
        }
    }
    if (!flag)
        puts("So crazy!!!");
    return 0;
}

E:煊哥的难题

太坑了。一开始我想了三种方法:第一种:叉积+直线重合,可以避免计算时的精度问题,但是比较难优化,只能暴力。第二种:斜率+截距的方法,可以进行map计数优化,但是计算时具有精度问题。第三种,还是斜率+截距,但是斜率用最简的式表示,不用直接相除。

但最后,各种权衡之下,我还是选择了第一种,然后就TLE,哎。

晚上看完jwGG讲解后,标程就是选择的第二种方法,那我就来说说第二组方法的题解吧。

  1. 选择map进行计数 :在斜率存在时分别记录斜率、y轴截距;在斜率不存在时,也就是垂直的时候,记录垂直的条数和x轴截距。

  2. 在输入坐标的时候进行更新答案。

  3. 直线与直线的关系有三种:第一种:重合;第二种:相交;第三种:平行。虽然直线重合,但是这个也计算相交。我们只需要把当前录入的所有直线 - 与当前直线平行的直线的数量。平行的直线=相同的斜率数量 - 相同的截距数量。
    a n s + = i − 1 − ( m a p [ 斜 率 ] − m a p [ 截 距 ] ) ans+=i-1-(map[斜率]-map[截距]) ans+=i1(map[]map[])

AC代码:

#include 
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100002;
int n, x[3], y[3];
double td, tk;
map<double, int> k;               //斜率->数量
map<pair<double, double>, int> d; //斜率,截距->数量,重合
map<int, int> d2;                 //垂直时候,截距->数量,重合
int cnt = 0;                      //垂直总个数
ll ans = 0;
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= 2; j++)
            scanf("%d%d", &x[j], &y[j]);
        if (x[1] == x[2]) //斜率不存在情况
        {
            td = x[1]; //x截距
            d2[td]++;
            cnt++;
            ans += (i - 1 - (cnt - d2[td]));
        }
        else
        {
            tk = (y[2] - y[1]) * 1.0 / (x[2] - x[1]); //k
            td = y[1] - tk * x[1];                    //y截距
            k[tk]++;
            d[{tk, td}]++;
            ans += (i - 1 - (k[tk] - d[{tk, td}]));
        }
    }
    printf("%lld\n", ans);
    return 0;
}

F: jwGG与yzMM的字符串

字符串的模拟题,先打解码表,最难想到的时解码表的打法,还有反推的过程。

  1. 我们选择利用二维数组来进行解码:密匙为x,原文是y,加密后的文件为z;这个式子代表用密匙x 来对加密后的文件z进行解密,还原成原文z

d [ x ] [ z ] = y d[x][z]=y d[x][z]=y

  1. 我们如何得到d数组呢?这时候就要预先打标。
  2. 加密是正向加密,解密就要反向解密。

AC代码:

#include 
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100002;
pair<int, int> p[maxn];
char a[55], b[130][130];
string s[1005];
void init()
{
    for (int i = 0; i <= 51; i++)
    {
        if (i <= 25)
            a[i] = 'A' + i;
        else
            a[i] = 'a' + i - 26;
    }
    for (int i = 0; i <= 51; i++)     //x
        for (int j = 0; j <= 51; j++) //y
        {
            int z = (i + j) % 52; //替换后的z,b中是存字符对应的ASCLLII
            b[a[i]][a[z]] = a[j];
        }
}
int main()
{
    ios::sync_with_stdio(false);
    int n, m;
    init();
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
        cin >> p[i].first >> p[i].second;
    for (int i = 1; i <= n; i++)
        cin >> s[i];
    for (int i = m; i >= 1; i--)
    {
        int y = p[i].second;
        int x = p[i].first;
        int len1 = s[x].size(), len2 = s[y].size();
        for (int i = 0; i < len2; i++)
            s[y][i] = (b[s[x][i % len1]][s[y][i]]);
    }
    for (int i = 1; i <= n; i++)
        cout << s[i] << endl;
    return 0;
}

G:jwGG与bwMM的字符串

暂时未作出,后续更新…


H:库特放书

一道披着二分的暴力查找答案的题,直接放出题人的题解。

​ 为什么直接暴力不用二分在时间上是可行的吧,因为我看所有通过代码都写的枚举容量的上界是sum,但是如果上界真的是sum的话在时间上理论上是不可行的,这一点上并不是我数据出水了,是它的下界可以视为max(ceil(sum / k),maxV),上界为ceil(sum / k)+MAXV 所以枚举次数为二者之差远达不到sum次,最多也不会超过1000次所以从下界while(1)向上寻找在时间上是可行的。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
int t, n, m;
int v[1002];
bool vis[1002];
bool check(int tt)
{
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= m; i++)
    {
        int sum = 0;
        for (int j = 1; j <= n; j++)
        {
            if (sum + v[j] <= tt && vis[j] == 0)
            {
                sum += v[j];
                vis[j] = 1;
            }
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (vis[i] == 0) //no
            return 0;
    }
    return 1;
}
int main()
{
    scanf("%d", &t);
    int k = 0;
    while (t--)
    {
        int sum = 0;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &v[i]);
            sum += v[i];
        }
        sort(v + 1, v + 1 + n, greater<int>());
        int l = sum / m, r = sum, ans;
        for (int i = l; i <= r; i++)
        {
            if (check(i))
            {
                ans = i;
                break;
            }
        }
        printf("Case #%d: %d\n", ++k, ans);
    }
    return 0;
}

你可能感兴趣的:(大一寒假培训)