2019年华南理工大学软件学院ACM集训队选拔赛 Round1

TIps: 1.所有代码中博主使用了scanf和printf作为输入输出 

    2.代码中使用了define LL long long 所以在声明变量的时候 LL其实就等价于long long

希望这两点不会成为读者看代码时候的障碍qwq 另外题目链接我会放在最后 如果需要请往下拖一拖

T1 

这道题其实就是单纯对数据进行排序,但是因为关键字不止一种(解题数m和罚时t) 并且输出的是队伍的名字(也就是序号)

而正常排序完之后我们就会发现 我们并不知道当前各个位置的数对应的队伍名是什么了(也就是其原本的位置信息已经在排序过程中丢失了)

这个时候我们发现 一个队伍有三种信息 解题数 罚时 以及队伍名 我们需要把这三种信息作为一个整体再将这个整体按照内部的信息(解题数以及罚时)进行排序

这个时候我们就需要用到结构体了 也就是这个东西

然后利用sort函数将结构体进行排序 但是这里我们需要自定义一个比较函数 按照自己的想法将结构体进行排序 这也就是代码中的cmp函数的作用

这里用到了三目运算符 不了解的同学可以百度一下他的用法 这里的cmp可以理解为先比较a和b的解题数,解题数多的在前,解题数一样再比较罚时,罚时多的在后

这样之后通过sort函数进行排序再按顺序输出id就可以解决问题了! 复杂度Ο(nlogn)

贴一下代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#define LL long long
using namespace std;
const int M=1e5+7;
int T,n;
struct node{int id,k,w;}e[M];//k表示解题数w表示罚时id表示队伍名
bool cmp(node a,node b){return a.k==b.k?a.w<b.w:a.k>b.k;}//自定义的比较函数
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            e[i].id=i;
            scanf("%d %d",&e[i].k,&e[i].w);
        }
        sort(e+1,e+1+n,cmp);//排序
        for(int i=1;i<=n;i++) printf("%d ",e[i].id); puts("");//puts("")作用只是换行
    }
    return 0;
}

 T2

这道题的话 我的做法和题解中的做法一样qwq 师兄讲的也非常清楚 这里就直接引用了

复杂度 Ο(n^2)

贴一下代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#define LL long long
using namespace std;
const int M=1e6+7;
int T,n,l,r;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        puts("YES");
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int now=(j-1)*n+(i+j-1<=n?i+j-1:i+j-1-n);
                printf("%d ",now);
            }
            puts("");
        }
    }
    return 0;
}

T3

这道题很明显贪心是不行的 因为上一步的决策可能影响到这一步的决策

例如你这一步如果按照贪心的做法选择了不坐电梯而是走路(也就是a[i]<b[i]+m)而下一步因为a[i+1]<b[i+1]+m仍然选择了走路

但是实际上可能a[i]+a[i+1]>b[i]+b[i+1]+m 所以贪心是不能解决问题的

其实这个问题是典型的动态规划(DP)不懂的同学可以了解一下 这里也给各位提供一下比较好的博客

入门的话一般会先看背包问题 https://blog.csdn.net/weixin_41162823/article/details/87878853

而与这道题类似的DP经典题目 如

导弹拦截 https://www.luogu.com.cn/problem/P1020

最长上升子序列 https://www.luogu.com.cn/problem/P3902

题解的话洛谷都有 不会可以康康

回到这道题本身 我们用ans[i][0],ans[i][1]分别表示到达第i层的最短时间

0表示上一层是坐电梯上来的 也就是这一层要坐电梯的话就不用等待了

1表示上一层是走路上来的 这一层如果要坐电梯就需要等待了

那么我们的转移方程就可以表示为

ans[i][0]=min(ans[i-1][0],ans[i-1][1])+b[i]

ans[i][1]=min(ans[i-1][0]+m,ans[i-1][1])+a[i]

最后输出min(ans[n][0],ans[n][1])问题就解决了qwq

复杂度 O(n)贴一下代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#define LL long long
using namespace std;
const int M=1e6+7;
int T,n,m;
LL a[M],b[M],ans[M][2];//0不坐1坐
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m); n--;
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
        ans[1][0]=b[1]; ans[1][1]=a[1]+m;
        for(int i=2;i<=n;i++){
            ans[i][0]=min(ans[i-1][0],ans[i-1][1])+b[i];//这一层走路去下一层
            ans[i][1]=min(ans[i-1][0]+m,ans[i-1][1])+a[i];//这一层坐电梯去下一层
        }
        printf("%lld\n",min(ans[n][0],ans[n][1]));
    }
    return 0;
}

T4

这道题可以理解为利用容斥原理实现吧

首先我们要明确 CAT子序列虽然不用连续 但是相对顺序是不能变的 也就是形如”TAC“是不符合喵喵字符串的定义的

那么我们考虑维护 C[i],A[i],T[i],CA[i],AT[i],CAT[i]六个数组

C[i]表示从1到i一共有几个C字符 A[i],T[i]同理 也就是维护一下前缀和

CA[i]就是维护1到i位置一共有多少种CA的组合 例如一个字符串CCAA 那么CA[4]=4

AT[i],CAT[i]同理

这样之后 对于每一个询问l r

答案 ans=CAT[r]-CAT[l-1]-CA[l-1]*(T[r]-T[l-1])-C[l-1]*(AT[r]-AT[l-1]-A[l-1]*(T[r]-T[l-1]));

CAT[l-1]指的是1到l-1的CAT串 这种位置的串明显是不合法的

CA[l-1]*(T[r]-T[l-1])指的是用 1 到 l-1 的“CA”和 l 到 r 的T组合而成的“CAT” 这也是不合法的子序列

C[l-1]*(AT[r]-AT[l-1]-A[l-1]*(T[r]-T[l-1]))指的是用 1 到 l-1 的“CA”和 l 到 r 的“T”组合而成的CAT 这明显也是不合法的子序列

除去所有的不合法串后我们便可以得到答案了 算法复杂度O(n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define LL long long
using namespace std;
const int M=2e6+7;
int len,m;
LL C[M],A[M],T[M],CA[M],AT[M],CAT[M];
char s[M];
int main(){
    scanf("%d %d",&len,&m);
    scanf("%s",s+1);
    for(int i=1;i<=len;i++){
        C[i]=C[i-1]; A[i]=A[i-1]; T[i]=T[i-1];
        if(s[i]==‘C‘) C[i]++;
        else if(s[i]==‘A‘) A[i]++;
        else T[i]++;
    }
    for(int i=1;i<=len;i++){
        CA[i]=CA[i-1]; AT[i]=AT[i-1];
        if(s[i]==‘A‘) CA[i]+=C[i];
        if(s[i]==‘T‘) AT[i]+=A[i];
    }
    for(int i=1;i<=len;i++){
        CAT[i]=CAT[i-1];
        if(s[i]==‘T‘) CAT[i]+=CA[i];
    }
    int l,r;
    for(int i=1;i<=m;i++){
        scanf("%d %d",&l,&r);
        LL ans=CAT[r]-CAT[l-1]-CA[l-1]*(T[r]-T[l-1])-C[l-1]*(AT[r]-AT[l-1]-A[l-1]*(T[r]-T[l-1]));
        printf("%lld\n",ans);
    }
    return 0;
}

T5

这道题确实算是最难的题目了 大部分同学暂时可以跳过 以后再回来写这道题会容易很多

所以继续往下看的同学我会理解为你是有一定图论基础的

这道题看起来是一道最短路问题 但是我们但我们发现只要每个 l[i] 足够小 r[i] 足够大,那么就可以成为一张完全图 这样无论是dijkstra还是spfa都会超时

我们先按照dijkstra的想法开始解决问题 但是我们发现 因为一点个去到其他任何一个他所能到达的点的距离都是一样的

那么我们将他丢进优先队列的时候 可以将他离1点的距离dis[i]加上他本身的c[i] 一起丢进优先队列中 那么每次从优先队列里拿出来的点就一定是当前所能更新的离1最近的点了

这样一来每个点只需要被更新一次就一定是最优解了 那么我们可以利用set维护当前未被更新的点 如果他被更新就将他从set中删除

这样就可以避免多次访问同一个点 复杂度就降下来了 因为每个点只会被访问到一次 所以复杂度是O(nlogn)

贴一下代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#include<queue>
#define LL long long
using namespace std;
const int M=2e6+7;
LL read(){
    LL ans=0,f=1,c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();}
    return ans*f;
}
LL T,n,w[M],ans[M],l[M],r[M];
struct node{
    LL id,d;
    bool operator <(const node&x)const{return x.d<d;}
};
priority_queue<node>q;
set<LL>S;
set<LL>::iterator L,R,C;
int main(){
    T=read();
    while(T--){
        while(!q.empty()) q.pop();
        S.clear();
        n=read();
        for(int i=2;i<=n;i++) ans[i]=-1,S.insert(i);
        for(int i=1;i<=n;i++) l[i]=read();
        for(int i=1;i<=n;i++) r[i]=read();
        for(int i=1;i<=n;i++) w[i]=read();
        ans[1]=0; q.push((node){1,w[1]});
        while(!S.empty()&&!q.empty()){
            node x=q.top(); q.pop();
            if(l[x.id]>r[x.id]) continue;
            L=S.lower_bound(x.id+l[x.id]);
            R=S.upper_bound(x.id+r[x.id]);
            while(L!=R){
                ans[*L]=x.d;
                q.push((node){*L,x.d+w[*L]});
                C=S.upper_bound(*L);
                S.erase(L);
                L=C;
            }
            L=S.lower_bound(x.id-r[x.id]);
            R=S.upper_bound(x.id-l[x.id]);
            while(L!=R){
                ans[*L]=x.d;
                q.push((node){*L,x.d+w[*L]});
                C=S.upper_bound(*L);
                S.erase(L);
                L=C;
            }
        }
        for(int i=1;i<n;i++) printf("%lld ",ans[i]); printf("%lld",ans[n]); puts("");
    }
    return 0;
}

当然对于不会STL的同学 我们也可以考虑利用并查集代替set的作用 也就是每次更新完一个点就将他指向右边的点(与右边的点合并)这样下次再访问到他就可以

直接跳到离他最近的右边的第一个点 同样可以解决多次访问同一个点的问题 当然因为博主比较懒 代码过两天再补上吧qwq

T1 http://110.64.92.203/contest/30/problem/1001

T2 http://110.64.92.203/contest/30/problem/1002

T3 http://110.64.92.203/contest/30/problem/1003

T4 http://110.64.92.203/contest/30/problem/1004

T5 http://110.64.92.203/contest/30/problem/1005

原文地址:https://www.cnblogs.com/yourinA/p/11973615.html

时间: 2024-10-09 03:34:15

2019年华南理工大学软件学院ACM集训队选拔赛 Round1的相关文章

2019 年「计算机科学与工程学院」新生赛 暨ACM集训队选拔赛 # 1

T1 请问这还是纸牌游戏吗 https://scut.online/p/567 这道题正解据说是方根 这里先放着等以后填坑吧qwq 但是由于这道题数据是随机的 所以其实是有各种水法的(但是我比赛根本没有想到任何水法qwq 第一种水法呢 因为数据随机 所以当数据大小变得比较大的时候 基本乘出来的数已经能覆盖1到P-1了 所以我们直接输出1和P-1就可以了 而数据比较小的时候就可以暴力计算了qwq include<cstdio> #include<cstring> #include&l

C 六学家的困惑 【YY】 (2019年华南理工大学程序设计竞赛(春季赛))

冲鸭,去刷题:https://ac.nowcoder.com/acm/contest/625/C 小六喜欢两全其美的事情,今天就正好有一个这样的机会. 小六面前有两根管子,管子里面放满了数字为1到9的小球.每次取球时,小六会先选择一根管子,再从这根管子的某一侧(左侧或右侧)取出一个球.在满足取球规则的情况下,他可以任意顺序取出所有小球.假如小六依次取出的球的编号为a1,a2,?,ana1,a2,?,an,则他最后就得到了一个形如¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯a1a2?ana1a

2019年华南理工大学程序设计竞赛(春季赛)A NB群友

https://ac.nowcoder.com/acm/contest/625/A 题意:给出一个区间范围 , 求有多少个数的每一位的积是在这个区间里面的 分析:没错了 ,就是记忆化暴力搜索 ,不断的枚举除 i ,i是2到9 , 看是否合法 ,  对于枚举到相同的状态的时候 , 后面的价值肯定和上一个状态是一样的 , 所以这样就好拉 #include<bits/stdc++.h> using namespace std; #define ll long long map<ll , ll

2019年华南理工大学程序设计竞赛(春季赛) K Parco_Love_String(后缀自动机)找两个串的相同字串有多少

https://ac.nowcoder.com/acm/contest/625/K 题意: 给出Q 个询问 i , 求 s[0..i-1] 与 s[i...len-1] 有多少相同的字串 分析: 给出了查询 , 容易想到先预处理出答案好吧 , 字符串的问题也容易想到后缀自动机 ,但是我们该怎么使用呢? 下面提供我的思路: 我们建立出SAM后 , 跑一边拓扑排序 ,根据SAM跑出来的拓扑排序的序列特性 , 我们可以求出 在当前状态st 的最大串字符出现的个数 for (int i = now; i

HDU2010省赛集训队选拔赛(校内赛) H

#define is unsafe Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 287    Accepted Submission(s): 178 Problem Description Have you used #define in C/C++ code like the code below? #include <stdio

开锁魔法II 哈尔滨理工大学第五届ACM程序设计竞赛

规律:a[i][j]=     1/i * a[i-1][j-1]      +      (i-1)/i * a[i-1][j];  (少一个盒子时使用j-1 次魔法的概率)   (少一个盒子时使用j次魔法的概率) 公式推导如下: 设a[i][j]为打开i个盒子正好需要j次魔法的情况. ① 1->1 ② 1->1 , 2->2;        两次 1->2 , 2->1;        一次 ③ 1->1 , 2->2 , 3->3;     三次 1-

[家里蹲大学数学杂志]第247期华南理工大学2013年高等代数考研试题参考解答

1 ($15'$) 设 $\bbP$ 是一个数域, $f(x),g(x)\in \bbP[x]$, 且 $\p (g(x))\geq 1$. 证明: 存在唯一的多项式序列 $f_0(x),f_1(x),\cdots,f_r(x)$, 使得对 $0\leq i\leq r$ 有 $\p (f_i(x))<\p (g(x))$ 或 $f_i(x)=0$, 且 $$\bex f(x)=\sum_{i=0}^r f_i(x)g^i(x). \eex$$ 证明: 由带余除法, $$\beex \bea f

HDU2010省赛集训队选拔赛(校内赛) G

lazy gege Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 948    Accepted Submission(s): 361 Problem Description Gege hasn't tidied his desk for long,now his desk is full of things. This mornin

2018.10.2浪在ACM 集训队第二次测试赛

2018.10.26 浪在ACM 集训队第二次测试赛 题目一览表(Green color indicate understand and Accept) 来源 考察知识点 完成时间 A 1273 海港(比赛过题) NOIP 普及组 2016 查分数组+二分 2018.10.26 D 1272 回问日期(比赛过题) NOIP 普及组 2016 思维? 2018.10.26 G 1268 扫雷游戏(比赛过题) NOIP 普及组 2015 暴力 or DFS 2018.10.26