2019CCPC江西省赛(重现赛)| 题目 & 题解

2019CCPC江西省赛(重现赛)在hdu进行

据说很简单

因为现场有多个队伍AK

然鹅

我在40分钟内AC两题之后 就卡在概率题 (后来就没做题目emmm)

先放官方题解 

然后慢慢补题

嗯!

题目:

https://pan.baidu.com/s/1nYWq8dWcS9csJDXGWTqHmA 提取码: 8pg4 复制这段内容后打开百度网盘手机App,操作更方便哦

题解:

https://pan.baidu.com/s/1bISMx_yH9yb5NJVirZQ61w 提取码: 4kn4 复制这段内容后打开百度网盘手机App,操作更方便哦

 

目录

K.签到题

F.签到题2

H.数学题

I.签到题3

J.最小公倍数

G.暴力模拟

D.暴力枚举

A.树的重心


 

K.签到题

解方程

已知 x = a+b y =a-b 求a*b 可以得知 a = (x+y)/2 b = x-a

#include 
#include 
#include 

using namespace std;

int main()
{
    int x,y;
    int a,b;
    scanf("%d%d",&x,&y);
    a = (x+y)/2;
    b = (x-a);
    printf("%d\n",a*b);
    return 0;
}

F.签到题2

因为只需要 a,v,i,n 四种字母

先判断这四种字母出现的个数

如果本身没有达到存在这四种字母 则不可能构成avin 答案是 0/1

达到四种字母都存在 则再次计算可能出现avin的次数 

PS:要注意分子与分母是否可以同分 (取分子分母得到公因子 同时除掉公因子

#include 
#include 
#include 
#include 
using namespace std;
int gcd(int a,int b)
{
    if (b==0)return a;
    return gcd(b,a%b);
}
int a[30];
int main()
{
    int t,x=0,y=0,sum=0;
    char m;
    while (~scanf("%d", &t)) {
            getchar();
        memset(a, 0, sizeof a);
        for (int i = 0; i < t; i++) {
            scanf("%c", &m);
            a[m - 'a' + 1] ++;
        }
        for (int i = 1; i <= 26; i++) {
            if (a[i] > 0) {
                sum =sum+a[i];
            }
        }

        x = a[1]*a[9]*a[14]*a[22];
        y = pow(sum,4);
          if (x>0)
          {
              if (gcd(x,y)>1)
                printf("%d/%d\n",x/gcd(x,y),y/gcd(x,y));
              else printf("%d/%d\n",x,y);
          }
            else printf("0/1\n");
    }
    return 0;
}

H.数学题

在【1,n】之间任取一个数 R, 然后在【1,R】中任取一个数得到 L, 得到区间【L1,R1】

再任取一次,得到区间【L2,R2】,求两区间有交集的概率对1e9+7取模

首先 理解什么是逆元 以及如何线性求逆元

参考:https://blog.csdn.net/qq_35416331/article/details/81059747

https://www.cnblogs.com/Judge/p/9383034.html#_label1

https://www.cnblogs.com/chy-2003/p/9656801.html#%E7%BA%BF%E6%80%A7%E6%B1%82%E9%80%86%E5%85%83

题目中的公式推导 参考博文:https://blog.csdn.net/toohandsomeieaseid/article/details/96757205

AC代码

#include
#include
#include 
#define ll long long
using namespace std;
const int maxn = 1e6 + 5;
const int p = 1e9 + 7;
ll inv[maxn], sum[maxn];
ll quickpow (ll x, ll y)
{
    ll res = x;
    ll ans = 1;
    while (y) {
        if (y & 1) {
            ans = (ans * res)%p;
        }
        res = (res * res)%p;
        y = y >> 1;
    }
    return ans;
}

int main()
{
    inv[0] = 1;
    inv[1] = 1;
    for (int i = 2; i < maxn; i++) {
        inv[i] = ((p - p / i) * inv[p % i]) % p;
    }
    memset(sum, 0, sizeof sum);
    for (int i = 1; i <= maxn; i++) {
        sum[i] = sum[i - 1] + inv[i];
    }
//
//    for (int i = 0; i < 10; i++) {
//        printf("%lld\n", sum[i]);
//    }

    int n;
    while (~scanf("%d", &n)) {
        ll ans = 0;
        for (int i = 1; i <= n; i++) {
            ll temp = 0;
            temp += i * inv[n] % p;
            temp = temp * ((sum[n] - sum[i] + p) % p) % p;
            temp = (temp % p) * inv[n] % p;
            ans = (ans + temp) % p;
        }
        ans += (3 + n) * quickpow(4 * n, p - 2);
        ans %= p;
        printf("%lld\n", ans);
    }
    return 0;
}

下面是我自己写的过程和理解

2019CCPC江西省赛(重现赛)| 题目 & 题解_第1张图片

 2019CCPC江西省赛(重现赛)| 题目 & 题解_第2张图片

 

I.签到题3

这道题理解错题目意思 然后一直想不通为啥就WA了

其实是 将第三位小数位进位到前一位 所增加的数之和(可以为负

代码如下:

#include 
#include 
#include 
#include 
using namespace std;

int main()
{
    int t, a, c;
    char b;

    while (~scanf("%d", &t)) {
            double sum = 0;
        for (int i = 0; i < t; i++) {
            scanf("%d%c%d", &a, &b, &c);
            c = c % 10;
            if (c <= 4) {
                sum -= 0.001 * c;
            } else {
                sum += 0.001 * (10-c);
            }
        }
        printf("%.3lf\n", sum);
    }
    return 0;
}

 

J.最小公倍数

题目意思:n个工厂和m个工人 给出每个工厂每天能处理的订单数a[i]

问 是否存在一种情况使得 每个工厂每天处理的订单数相同 且所有人都要在工作 (即 没有多余的工人不工作)

存在则输出“Yes”以及每一个工厂的人数

否则 就输出“No”

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
ll a[10005], b[10005];
ll gcd(ll a, ll b)
{
    return b ? gcd(b, a % b) : a;
}
int main()
{
    int n ;
    ll m;
    while (~scanf("%d%lld", &n, &m)) {
        ll ans = 1, sum = 0;
        for (int i = 0; i < n; i++) {
            scanf("%lld", &a[i]);
        }
        ll mm = gcd(a[0], a[1]);
        ans = a[0] * a[1] / mm;
        for (int i = 2; i < n; i++) {
            mm = gcd(ans, a[i]);
            ans = ans * a[i] / mm;
        }
        for (int i = 0; i < n; i++) {
            b[i] = ans / a[i];
            sum += b[i];
        }
//        for (int i=0;i

注意:题目中m的范围是 1~ 10^18,而不是1018(辣鸡题面 小声bb

所以要用 long long  保存数据

还有在计算最小公倍数的时候 要注意

是先求出前面的最小公倍数 与后面的数求公约数 再次得到最小公倍数

举个栗子

3 4 6 8 的最小公倍数是24 而不是3 * 4 * 6 * 8 

 

G.暴力模拟

参考:https://www.cnblogs.com/Dup4/p/11223762.html

第一遍读题目没有弄清题意

看了题解才知道是 在一个十字路口,有nn辆东西走向的车,他们会在 a[i] 时刻到达,有m辆南北走向的车,他们会在 b[i] 时刻到达。问需要让m辆南北走向的车整体等待多少秒,使得他们的开始行动之后不会和东西走向的车相撞?

找到可以取到 b[j] - a[i] 不等于 X 的最小值

数据范围都是 1~1000 可以直接暴力枚举

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
ll const mod = 998244353;
int a[2005], b[2005];
int main()
{
    int n, m, x;
    while (~scanf("%d%d", &n, &m)) {
        memset(a, 0, sizeof a);
        memset(b, 0, sizeof b);
        for (int i = 0; i < n; i++) {
            scanf("%d", &x);
            a[x] = 1;
        }
        for (int i = 0; i < m; i++) {
            scanf("%d", &b[i]);
        }
        int p = 0;
        for (int i = 0; i < m; i++) {
            if (a[b[i] + p] == 1) {
                p++;
                i = 0;//全部重新判断一遍
            }
        }
        printf("%d\n", p);
    }
    return 0;
}

D.暴力枚举

我是傻子嘛555 为啥重现赛的时候不好好做题

感觉没有很难啊QAQ

题目大意:

形成 wave 的条件

1)它至少包含两个元素;

2)奇数位置的所有元素都是相同的;

3)偶数位置的所有元素都是相同的;

4)奇数位置的元素与偶数位置的元素不同。

由1)、2)、3) 可以推断出 wave 中含有的数字有且仅有两种 

求所给数组中 形成wave 的最长的长度

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
ll const mod = 998244353;
int a[100005], pos[103][100005], num[103];
int main()
{
    int n, c;
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= c; i++) {
            if (a[k] == i) {
                num[i]++;
                pos[i][num[i]] = k;
            }
        }
    int d = 0;
    int flag;
    for (int i = 1; i < c; i++)
        for (int j = i + 1; j <= c; j++) {
            if (num[i] > 0 && num[j] > 0) {
                int ans = 0, sum = 0;
                int m  = min(pos[i][1], pos[j][1]);
                int mm = max(pos[i][num[i]], pos[j][num[j]]);
                if (m == pos[i][1]) {
                    flag = 1;
                    for (int k = m; k <= mm; k++) {
                        if (a[k] == i && flag == 1) {
                            flag = 0;
                            sum++;
                        }
                        if (a[k] == j && flag == 0) {
                            flag = 1;
                            sum++;
                        }
                        d = max(d, sum);
                    }
                } else {
                    flag = 0;
                    for (int k = m; k <= mm; k++) {
                        if (a[k] == j && flag == 0) {
                            flag = 1;
                            sum++;
                        }
                        if (a[k] == i && flag == 1) {
                            flag = 0;
                            sum++;
                        }
                        d = max(d, sum);
                    }
                }
            }
        }
    printf("%d\n", d);
    return 0;
}

因为我不喜欢用数据结构(其实是太菜了不会QAQ

看了别人的题解还是不懂 然后硬算暴力写了一个多小时emmm 终于AC了

我的代码思想就是 把所有数字的位置存起来 然后枚举某两个数字的位置 判断是否间隔着出现 将他们计数取最大值

数组用的比较多 不过本身数据不大 没有炸

 

A.树的重心

 

参考:https://blog.csdn.net/Ratina/article/details/96753811

 

C.数论

暂时不会 

留一个坑……

 

参考:https://www.cnblogs.com/Dup4/p/11223762.html

 

 

你可能感兴趣的:(比赛周记)