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

Posted yourina

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019 年「计算机科学与工程学院」新生赛 暨ACM集训队选拔赛 # 1相关的知识,希望对你有一定的参考价值。

T1 请问这还是纸牌游戏吗 https://scut.online/p/567

这道题正解据说是方根 这里先放着等以后填坑吧qwq

但是由于这道题数据是随机的 所以其实是有各种水法的(但是我比赛根本没有想到任何水法qwq

第一种水法呢 因为数据随机 所以当数据大小变得比较大的时候 基本乘出来的数已经能覆盖1到P-1了 所以我们直接输出1和P-1就可以了

而数据比较小的时候就可以暴力计算了qwq 

技术图片
include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int M=2e6+7;
int read(){
    int 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 mod,n,m;
int v1[M],v2[M],cnt1,cnt2;
LL h1[M],h2[M];
int main(){
    LL x;
    mod=read(); n=read(); m=read();
    for(int i=1;i<=n;i++){
        x=read();
        if(!v1[x]) v1[x]=1,h1[++cnt1]=x;
    }
    for(int i=1;i<=m;i++){
        x=read();
        if(!v2[x]) v2[x]=1,h2[++cnt2]=x;
    }
    if(cnt1*cnt2>(5e7)){printf("1 %d
",mod-1); return 0;}
    LL ans1=mod-1,ans2=0;
    for(int i=1;i<=cnt1;i++)
        for(int j=1;j<=cnt2;j++){
            ans1=min(ans1,h1[i]*h2[j]%mod);
            ans2=max(ans2,h1[i]*h2[j]%mod);
        }
    printf("%lld %lld
",ans1,ans2);
    return 0;
}
View Code

第二种写法呢 我们就可以从枚举P-1开始枚举答案最大是多少 然后枚举每一个a i 看是否存在b i 使得ab%P==枚举的答案

这里利用费马小定理预处理好了每一个a i对应的逆元fv i 所以可以根据枚举的答案和a i直接计算出b所对应的值

然后判断这个值是否存在就可以啦

这样子写的话想要卡掉其实是非常困难的 因为如果答案在中间部分(需要枚举大概n/2次答案()

那么一定会存在很多重复的数 而我们一开始就可以先把重复数去掉(也就是去重)

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int M=2e6+7;
int read(){
    int 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 mod,n,m;
int v1[M],v2[M],cnt1,cnt2;
LL h1[M],h2[M],fv[M];
LL qmod(LL a,LL b){
    LL ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1; 
        a=a*a%mod;
    }
    return ans;
}
int main(){
    LL x,ans1,ans2,f;
    mod=read(); n=read(); m=read();
    for(int i=1;i<=n;i++){
        x=read();
        if(!v1[x]){
            v1[x]=1;
            h1[++cnt1]=x;
            fv[i]=qmod(x,mod-2);
        }
    }
    for(int i=1;i<=m;i++){
        x=read();
        if(!v2[x]) v2[x]=1,h2[++cnt2]=x;
    }
    for(f=0,ans1=1;;ans1++){
        for(int i=1;i<=cnt1;i++)
        if(v2[ans1*fv[i]%mod]){f=1; break;}
        if(f) break;
    }
    for(f=0,ans2=mod-1;;ans2--){
        for(int i=1;i<=cnt1;i++)
        if(v2[ans2*fv[i]%mod]){f=1; break;}
        if(f) break;
    }
    printf("%lld %lld
",ans1,ans2);
    return 0;
}
View Code

T2 请问你喜欢跑步吗 https://scut.online/p/568

这道题就很明显是道水题了 找出每秒所有面包店中价值最大的面包 然后求和就可以了

技术图片
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) x&-x
using namespace std;
const int M=507;
int read(){
    int 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;
}
int n,m;
LL ans,s[M][M],k[M][M];
int main(){
    n=read(); m=read();
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) s[i][j]=read();
    for(int i=1;i<=m;i++){
        LL sum=0;
        for(int x=1;x<=n;x++) sum=max(sum,s[x][i]);
        ans+=sum;
    }
    printf("%lld
",ans);
    return 0;
}
View Code

T3 请问穿着熊厉害吗 https://scut.online/p/569

这道题枚举到 √n就可以了 

n到n-1 会对应大于 √n的另一段 具体的话通过手动模拟应该能有所体会 细节还是蛮多的

技术图片
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<set>
#define LL long long
using namespace std;
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,q,ans,last,G,x;
int main(){
    T=read();
    while(T--){
        n=read();
        if(n==1){puts("2"); continue;}
        q=2*n; ans=q+n; last=n;
        //printf("%lld
",ans);
        for(x=3;x*x<=q;x++){
            ans+=q/x;
            if(q%x) G=q/x; 
            else G=q/x;
            ans=ans+(x-1)*(last-G);
            //printf("%d %d
",last,G);
            last=G;
        }
        ans+=(last-x+1)*(x-1);
        printf("%lld
",ans);
        //printf("%lld %lld
",last,x);
    }
    return 0;
}
View Code

T4 请问机关锁打得开吗 https://scut.online/p/570

这道题9个锁 每个锁4种状态 一共也就2^18种状态 利用二进制来表示每一种状态

比如第一位和第二位表示第一个锁的状态也就是

00表示状态1

01表示状态2

10表示状态3

11表示状态四

以此类推 然后用bfs扫一遍就可以得到答案了

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int Q=17,P=(1<<19)+7;
int read(){
    int 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;
}
int S,p[Q],s[Q][Q];
int G(int k,int x){
    int w=0;
    if(k&(1<<(2*x))) w+=2;
    if(k&(1<<(2*x-1))) w+=1;
    return w;
}
void nsp(int &k,int x,int w){
    if(k&(1<<(2*x)))    k-=(1<<(2*x));
    if(k&(1<<(2*x-1)))  k-=(1<<(2*x-1));
    if(w&1) k+=(1<<(2*x-1));
    if(w&(1<<1)) k+=(1<<(2*x));
}
int vis[P],d[P],ans=-1;
queue<int>q;
void bfs(){
    q.push(S); vis[S]=1; d[S]=0;
    while(!q.empty()){
        int MM=q.front(); q.pop();
        if(!MM){ans=d[MM]; break;}
        for(int x=1;x<=9;x++){
            int M=MM,w=G(M,x),T=s[x][w],l=G(M,T);
            w=(w+1)%4; l=(l+1)%4;
            nsp(M,x,w); nsp(M,T,l);
            if(!vis[M]) vis[M]=1,d[M]=d[MM]+1,q.push(M);
        }
    }
}
int main(){
    for(int i=1;i<=9;i++){
        p[i]=read()-1;
        for(int j=0;j<=3;j++) s[i][j]=read();
    }
    for(int i=1;i<=9;i++) nsp(S,i,p[i]);
    bfs(); printf("%d
",ans);
    return 0;
}
View Code

T5 请问直方图里能画矩形吗 https://scut.online/p/571

这道题的话 对每一个位置 找出他向左向右所能延申的最远距离就好了

可以用单调栈维护

考虑向左的延申的情况 我们考虑从1位置扫到i(当前)位置

如果一个位置j 他的高度h【j】比你大 并且他位置在你之前(j<I)那么他就可以被忽略

因为要从右边经过你的话 最大高度也就只能是h【i】

向右同理

通过这样的方法我们就可以维护出每一个点向左向右所能延申的最远距离了

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define LL long long
using namespace std;
const int M=1e6+7;
int read(){
    int 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 n,h[M],stk[M],top,l[M],r[M],ans;
int main(){
    n=read();
    stk[0]=0;
    for(int i=1;i<=n;i++) h[i]=read();
    for(int i=1;i<=n;i++){
        while(top&&h[stk[top]]>=h[i]) top--;
        l[i]=stk[top]+1;
        stk[++top]=i;
        //for(int x=1;x<=top;x++) printf("%d ",h[stk[x]]); puts("");
    }
    top=0; stk[0]=n+1;
    for(int i=n;i>=1;i--){
        while(top&&h[stk[top]]>=h[i]) top--;
        r[i]=stk[top]-1;
        stk[++top]=i;
    }
    //for(int i=1;i<=n;i++) printf("%d %d
",l[i],r[i]);
    for(int i=1;i<=n;i++) ans=max(ans,(r[i]-l[i]+1)*h[i]);
    printf("%lld
",ans);
    return 0;
}
View Code

当然我比赛的时候脑子一抽就用了set维护

就是将所有的点按从小到大排序 然后从最小开始枚举 将枚举到的点的位置丢到set的里面

这样到当前点时 所有高度比他小的点就已经被丢到set里面的 你就只需要找位置离你最近的点是谁就好了

这个用lower_bound实现的okay了 当然这样写复杂度要多一个log

技术图片
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<set>
#define LL long long
using namespace std;
const int M=1e6+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 n,r[M],ans,sum[M],l[M];
struct node{LL id,h;}e[M];
bool cmp(node a,node b){return a.h==b.h?a.id<b.id:a.h<b.h;}
set<LL>q;
set<LL>::iterator It;
int main(){
    n=read();
    for(int i=1;i<=n;i++) e[i].h=read(),e[i].id=i;
    sort(e+1,e+1+n,cmp);
    for(int i=1;i<=n;i++){
        if(q.empty()) r[e[i].id]=n+1;
        else{
            It=q.end();
            if(*--It<e[i].id) r[e[i].id]=n+1;
            else r[e[i].id]=*q.upper_bound(e[i].id);
        }
        //sum[e[i].id]=e[i].h*(r[e[i].id]-e[i].id);
        q.insert(e[i].id);
    }
    q.clear();
    for(int i=1;i<=n;i++){
        if(q.empty()) l[e[i].id]=0;
        else{
            It=q.end();
            if(*--It<(-e[i].id)) l[e[i].id]=0;
            else l[e[i].id]=*q.upper_bound(-e[i].id)*-1;
        }
        //sum[e[i].id]=e[i].h*(r[e[i].id]-e[i].id);
        q.insert(-e[i].id);
    }
    //for(int i=1;i<=n;i++) printf("%d %d
",l[i],r[i]);
    for(int i=1;i<=n;i++) sum[e[i].id]=e[i].h*(r[e[i].id]-l[e[i].id]-1);
    for(int i=1;i<=n;i++) ans=max(ans,sum[i]);
    printf("%lld
",ans);
    return 0;
}
View Code

T5  留坑待补

T6 留坑待补

T7  请问这是鸽子吗 https://scut.online/p/574

这道题的话 一共就26种字母 我们考虑将26种字母单独维护

对于每个字母 用一个树状数组维护前缀一共有多少个相同的xx(x为当前字母)对 当然 如aaa算两对

xx对以后一个位置作为代表参与计算

然后每次修改则涉及四次修改  删除两次 插入两次 看是否有xx对被破坏 或者是否有xx对形成

然后询问的时候26种各求一次就okay了 当然求和的时候注意细节 如原本为aaaaa的一段

求后三个位置(即aaa)的aa对数时 求出来应为3对 但是因为第一个a的前一位被切断了 所以需要减去1 即为2对

这个细节在求和时是需要注意的

技术图片
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) x&-x
using namespace std;
const int M=1e6+7,N=2e5+7;
int read(){
    int 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;
}
char c[M];
int m,n;
int s[26][N];
void add(int w,int x,int v){
    //printf("%d %d %d
",w,x,v);
    while(x<=n){
        s[w][x]+=v;
        x+=lowbit(x);
    }
    //for(int i=1;i<=n;i++) printf("%d ",s[w][i]); puts("");
}
int p_sum(int w,int x){
    int ans=0;
    while(x) ans+=s[w][x],x-=lowbit(x);
    return ans;
}
int main(){
    scanf("%s",c+1);
    n=strlen(c+1);
    for(int i=2;i<=n;i++) if(c[i]==c[i-1]) add(c[i]-a,i,1);//puts("qwq");
    //for(int i=1;i<=n;i++) printf("%d ",s[1][i]);
    //printf("%d
",p_sum(1,15));
    int op,x,y,q;
    m=read();
    while(m--){
        op=read(); x=read();
        if(op==1){
            q=getchar();
            if(c[x]==q) continue;
            if(c[x]==c[x-1]) add(c[x]-a,x,-1);
            if(c[x]==c[x+1]) add(c[x]-a,x+1,-1);
            c[x]=q;
            if(c[x]==c[x-1]) add(c[x]-a,x,1);
            if(c[x]==c[x+1]) add(c[x]-a,x+1,1);
        }
        else{
            y=read();
            int ans=0;
            for(int i=0;i<26;i++){
                int now=p_sum(i,y)-p_sum(i,x-1);
                //printf("%d %d
",p_sum(i,y),p_sum(i,x-1));
                if(now==1&&c[x]==(a+i)&&c[x]==c[x-1]) now=0;
                if(now) ans+=1;
            }
            printf("%d
",ans);
        }
    }
    return 0;
}
View Code

T8 请问我可以用左手吗 https://scut.online/p/575

这道题就是单纯的高精度加法了

技术图片
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
#define lowbit(x) x&-x
using namespace std;
int read(){
    int 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;
}
char a[507],b[507],c[507],d[507];
int la,lb,lc,ld,mx,now;
int main(){
    scanf("%s",a+1); la=strlen(a+1);
    scanf("%s",b+1); lb=strlen(b+1);
    scanf("%s",c+1); lc=strlen(c+1);
    mx=max(la,lb); mx=max(lc,mx);
    while(mx){
        int sum=now;
        mx--;
        if(la) sum+=(a[la]-0),la--;
        if(lb) sum+=(b[lb]-0),lb--;
        if(lc) sum+=(c[lc]-0),lc--;
        now=sum/10;
        d[++ld]=sum%10;
    }
    if(now) d[++ld]=now;
    for(int i=ld;i>=1;i--) printf("%d",d[i]);
    return 0;
}
View Code

T9 请问你强吗 https://scut.online/p/576

这道题就是单纯判断某些字母的出现次数

技术图片
https://scut.online/p/576
View Code

 

以上是关于2019 年「计算机科学与工程学院」新生赛 暨ACM集训队选拔赛 # 1的主要内容,如果未能解决你的问题,请参考以下文章

2019年安徽大学ACM/ICPC实验室新生赛(公开赛)D 不定方程

2016广东工业大学新生杯决赛网络同步赛暨全国新生邀请赛

2019中国工程机器人大赛暨国际公开赛人工智能项目外骨骼赛总结

2016广东工业大学新生杯决赛网络同步赛暨全国新生邀请赛 题解&源码

BUUCTF-[ACTF2020 新生赛]Exec 记录

2019年广东工业大学腾讯杯新生程序设计竞赛(同步赛)