2019年CCPC-江西省赛(重现赛)部分题解

前言

下了场雪,差点没给学校埋了,教学楼时不时停个电、断个网的。
这些我都能忍,为啥还能有只把灯的电给停了的呢?差点就以为是自己给机房灯干坏了。

在这里插入图片描述
这让我想到了之前宿舍楼的光缆被挖掘机挖断的事情,给大家伙逗乐了属实是。


1004 - Wave(枚举/动态规划)

比赛链接:https://acm.dingbacode.com/submit.php?pid=6570

题目大意

如果一个序列 a a a满足条件:

  1. 序列 a a a中处于奇数位置的数字全都是 x x x
  2. 序列 b b b中处于偶数位置的数字全都是 y y y
  3. x ≠ y x≠y x=y

那么我们称序列 a a a是一个波动序列。

现在给出一个长度为n的序列s,序列s中所有的元素都小于c。
请求出序列a中最长的波动子序列的长度。

思路

由于 c c c最大才是 100 100 100,所以我们可以直接用枚举解决。
v e c t o r vector vector存储每个数字出现的位置,然后枚举奇数位与偶数位会是哪两个数字。

但如果 c c c的范围加大,博主建议用动态规划做,会省下不少时间。
但博主是个dp的fw,所以大家可以看一下https://blog.csdn.net/dabaitutunaitang/article/details/121238923对于B题的讲解,讲解的还是十分到位的。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;

int a[maxn];

int main()
{
    
    
    int n,c;
    while(cin>>n>>c)
    {
    
    
        vector<int> arr[105];
        int ans=-1;
        for(int i=1; i<=n; i++)
        {
    
    
            cin>>a[i];
            arr[a[i]].push_back(i);
        }
        for(int i=1; i<=100; i++)
        {
    
    
            if(arr[i].size()==0)
                continue;
            int len1=arr[i].size();
            for(int j=1; j<=100; j++)
            {
    
    
                if(arr[j].size()==0||i==j)
                    continue;
                int len2=arr[j].size();
                int l1=0,l2=0;
                int lastpos=0;
                int cnt=0;
                while(true)
                {
    
    
                    while(l1<len1&&arr[i][l1]<lastpos)
                        l1++;
                    if(l1==len1) break;
                    lastpos=arr[i][l1];
                    cnt++;
                    while(l2<len2&&arr[j][l2]<lastpos)
                        l2++;
                    if(l2==len2) break;
                    lastpos=arr[j][l2];
                    cnt++;
                }
                ans=max(ans,cnt);
            }
        }
        cout<<ans<<endl;

    }
}

1006 - String(思维)

比赛链接:https://acm.dingbacode.com/submit.php?pid=6572

题目大意

给出一个长度为n的字符串。
每次从字符串中随机取出一个字符,再放回去。
一共取4次,请问按照取字符的顺序组成的字符串有多少几率是"avin"呢?
请输出最简化分数形式。

若概率为0,则输出"0/1"。

思路

答案:
( n u m a numa numa:字符 a a a的个数, n u m v numv numv:字符 v v v的个数, n u m i numi numi:字符 i i i的个数, n u m n numn numn:字符 n n n的个数)
在这里插入图片描述
答案需要是最简分式,所以还需要求最大公约数。

AC代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e3+100;

ll a[maxn];
ll b[maxn];

int main()
{
    
    
    int n;
    while(cin>>n)
    {
    
    
        string ss;
        map<char,int> mp;
        cin>>ss;
        for(int i=0;i<n;i++)
            mp[ss[i]]++;
        ll y=n*n*n*n;
        ll x=mp['a']*mp['v']*mp['i']*mp['n'];
        if(x==0) cout<<"0/1"<<endl;
        else{
    
    
            ll z=__gcd(x,y);
            x/=z;
            y/=z;
            cout<<x<<"/"<<y<<endl;
        }
    }
}

1007 - Traffic(枚举)

比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6573

题目大意

Avin正在观察一个十字路口来往的车辆。
他发现有 n n n辆车是由东向西行驶,有 m m m辆车是由北向南行驶,每辆车都有自己到达十字路口的时间点。我们假设穿过十字路口不需要时间。
如果有一辆东→西方向的车在T时刻到达十字路口,还有一辆北→南方向的车也在T时刻到达十字路口,则两车可能发生碰撞,出现事故。
于是我们规定,所有北→南方向的车在到达十字路口时都需要等待 X X X个时刻才能继续行驶,以避免发生事故。

现在请你求出X的最小值是多少。

思路

由于题目中所有车到达的时刻ti(所有的aibi统称为ti)都在 1000 1000 1000以内,所以我直接枚举了 x x x的值,最多就是 1000 1000 1000而已。

每次只需要看北→南方向的车的到达时间加上 x x x之后是否还会出现在数组 a a a即可。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;

int a[1050];
int b[1050];

int main()
{
    
    
    int n,m;
    while(cin>>n>>m){
    
    
        for(int i=1;i<=n;i++)
            cin>>a[i];
        sort(a+1,a+1+n);
        for(int i=1;i<=m;i++)
            cin>>b[i];
        int x=0;
        while(true){
    
    
            int flag=1;
            for(int i=1;i<=m;i++){
    
    
                int pos=lower_bound(a+1,a+1+n,b[i]+x)-a;
                if(a[pos]==b[i]+x){
    
    
                    flag=0;
                    break;
                }
            }
            if(flag) break;
            x++;
        }
        cout<<x<<endl;
    }
}

1008 - Rng(规律+数论+逆元)

比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6574

题目大意

现在给出数字n。
Avin先在 [ 1 , n ] [1,n] [1,n]中随机挑出一个数字 r r r,再从 [ 1 , r ] [1,r] [1,r]中随机挑出一个数 l l l。这样Avin就获得了一个区间 [ l , r ] [l,r] [l,r]
接下来Avin重复刚才的操作,又获得了一个区间 [ l 2 , r 2 ] [l_2,r_2] [l2,r2]

问:两个区间相交的概率是多少?
假设这个值为 a b \frac{a}{b} ba,请输出 a b \frac{a}{b} ba% ( 1 e 9 + 7 ) (1e9+7) (1e9+7)的值。

思路

首先是求出 a b \frac{a}{b} ba的值。
我把 n n n1,2,3,4,5的情况自己模拟了一遍,发现是 ( n + 1 ) ( 2 ∗ n ) \frac{(n+1)}{(2*n)} (2n)(n+1),尝试了一下就过了。
想看详细推导过程可以看这个https://blog.csdn.net/qq_42671946/article/details/96756347
大佬nb,博主就是个靠运气过题的屑罢了。

剩下的就是求整体。
一开始没看出来 n = = 2 n==2 n==2的时候为啥是 750000006 750000006 750000006,自己求的明明是 3 4 \frac{3}{4} 43,怎么可能这么大。
后来想起了逆元,然后就明白了。

  • 1 a \frac{1}{a} a1% m o d mod mod== a ( m o d − 2 ) a^{(mod-2)} a(mod2),前提是 a a a m o d mod mod互质。

逆元证明详见https://blog.csdn.net/acdreamers/article/details/8220787

我们又知道, 1 e 9 + 7 1e9+7 1e9+7是个质数,质数和任何数都互质,代码就出来了。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;

ll quickpower(ll base,ll power)
{
    
    
    ll result=1;
    while(power){
    
    
        if(power&1) result=(result*base)%mod;
        power>>=1;
        base=(base*base)%mod;
    }
    return result;
}
int main()
{
    
    
    ll n;
    while(cin>>n){
    
    
        cout<<(n+1)*quickpower(n*2,mod-2)%mod<<endl;
    }
}

1009 - Budget(思维)

比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6575

题目大意

给出 n n n个浮点数,每个浮点数都有 3 3 3个小数位。我们现在想要让所有的浮点数从 3 3 3位小数变为 2 2 2位小数( 1.001 → 1.00 1.001→1.00 1.0011.00),这就要涉及到最后一位的取舍。

设最后一位小数为 x x x

  • x < 5 x<5 x<5,则需要舍弃掉 x x x,付出 − 0.00 x -0.00x 0.00x的代价;
  • x > = 5 x>=5 x>=5,则需要进位,符出 0.00 ( 10 − x ) 0.00(10-x) 0.00(10x)的代价;

请算出把 n n n个浮点数全部变成 2 2 2位小数所需要付出的代价总和。

思路

一开始用 d o u b l e double double类型进行输入与处理,后来发现精度无法保证。
苦思一番之后想到可以用字符串存储,既解决了数据大小的问题,又方便我取最后一位数的值。

AC代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e3+100;

ll a[maxn];
ll b[maxn];

int main()
{
    
    
    int n;
    while(scanf("%d",&n)!=EOF)
    {
    
    
        double ans=0;
        for(int i=0;i<n;i++)
        {
    
    
            char s[50];
            scanf("%s",s);
            int m=s[strlen(s)-1]-'0';
            if(m>4)
                ans+=10-m;
            else
                ans-=m;
        }
        printf("%.3f\n",ans/(1000*1.0));
    }
}

1010 - Worker(思维+数学)

比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6576

题目大意

现在有 n n n个车间, m m m个工人。
环境会影响工人的积极性,在第 i i i个车间工作的工人可以完成 a i a_i ai个任务(是指一个人就能完成 a i a_i ai个任务)。

现在请你求出一个分配工人的方案,使得每个车间最后完成的任务总数都是相同的。
每个工人只能且必须被分配到一个车间。
如果没有方案则输出" N O NO NO"。

思路

首先找特殊情况:

  • n==1,此时我们直接让所有工人都去那一个车间即可;
  • n>m,这就无法保证每个车间至少要有一个工人的前提(为什么是前提自己想),输出NO

接下来讨论其他情况。
首先我们假设最后所有车间完成的任务数为ans,那么ans就应该是 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1a2...an的一个公倍数。
那么我们就可以先求出 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1a2...an的最小公倍数min_ans,用min_ansai就可以得到每个车间最少需要的人数bi,紧接着求出最少的总工人数sum。此时如果m是sum的倍数,那说明我们只需要再给每个车间分配 ( m s u m − 1 ) ∗ b i (\frac{m}{sum}-1)*bi (summ1)bi个人即可;否则就无法分配。

AC代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e3+100;

ll a[maxn];
ll b[maxn];

ll gcd(ll x,ll y){
    
    
    return y==0?x:gcd(y,x%y);
}

int main()
{
    
    
    ll n,m;
    while(cin>>n>>m)
    {
    
    
        for(int i=1; i<=n; i++)
            cin>>a[i];
        if(n==1)
            cout<<"Yes"<<endl<<m<<endl;
        else if(n>m)
            cout<<"No"<<endl;
        else
        {
    
    
            ll ans=a[1];
            for(int i=2;i<=n;i++){
    
    
                ll x=__gcd(ans,a[i]);
                ans=ans*a[i]/x;
            }
            ll sum=0;
            for(int i=1; i<=n; i++)
            {
    
    
                b[i]=ans/a[i];
                sum+=b[i];
            }
            if(m%sum==0)
            {
    
    
                ll k=m/sum;
                cout<<"Yes"<<endl;
                for(int i=1; i<=n; i++)
                {
    
    
                    if(i!=1)
                        cout<<" ";
                    cout<<b[i]*k;
                }
                cout<<endl;
            }
            else
                cout<<"No"<<endl;
        }
    }
}

1011 - Class(水题+初中数学)

比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6577

题目大意

已知: a + b = x , a − b = y a+b=x,a-b=y a+b=xab=y
问给出 x 、 y x、y xy的值,能否求出 a ∗ b a*b ab

思路

一开始读错题意了,读成给出a、b,求出x*y,上来就先WA了一发,ԾㅂԾ,
这要是真在比赛,估计我就被队友生吃了。
在这里插入图片描述
运用初中数学原理 (x+y)^2-(x-y)^2==4xy即可解决。

AC代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e5+100;

int main()
{
    
    
    int a,b;
    cin>>a>>b;
    cout<<(a*a-b*b)/4<<endl;
}

后话

感谢阅读,希望能对你产生一点用处。

以下台词取自《银魂》第87集:
(十四的内心太细腻了,总悟有点姐控了(●’◡’●))
(87集里有好多十四帅气的镜头,爱了爱了)
(搭嘎,我还是更喜欢我的伊丽莎白(✿◡‿◡))

在这里插入图片描述

"我没资格指责你"
"我也跟你一样半斤八两,以前做了不少过分的事"
"到头来,在她死前要砍了她老公"
"我真是有够过分的"
"我,只不过是"
"想让心爱的女人,得到幸福而已"
"在这种地方挥刀的我,是做不到的"
"我只希望她能跟普通人组成平凡的家庭"
"平凡的生儿育女,平凡的活下去罢了"
"仅是如此而已"

吾日三省吾身:日更否?刷题否?快乐否?
更新了,但不是日更;已刷;激动
路漫漫其修远兮,吾将上下而求索

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45750296/article/details/121230293