2020 年 “联想杯”全国高校程序设计在线邀请赛

左手太顶了,369太9了,水子哥又成白开水了!!!

传过去送的门

感觉还是难题做不了,水题做不快,都不知道怎么提升了。但先走下去吧,等一波奇迹团。

这次才做了7个,太菜了(还是在我360秘书的帮助下),剩下的看能力补。。。

B.Bamboo Leaf Rhapsody 

题意大概就是给你n个三维的点,找离(0,0,0)最近的点的距离。for一遍。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    int n;
    double ans=1e9+11;
    scanf("%d",&n);
    while(n--){
        double x,y,z;
        scanf("%lf%lf%lf",&x,&y,&z);
        ans=min(ans,sqrt(x*x+y*y+z*z));
    }
    printf("%.3f
",ans);
    return 0;
} 
签个到

A.Archmage

一开始有n>=x+y个mana,然后每秒会增加y个mana,并且每秒可以花费x个mana换1个Water Element,换操作是在增加操作之前的。问m秒后最多能有几个Water Element?

当x<=y,一直有mana课进行交换,x<y时,m-1秒增加的mana都有可能用上。

在m跟(n+(m-1)*y)/x取个最小值就好。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        ll n,m,x,y;
        scanf("%lld%lld%lld%lld",&n,&m,&x,&y);
        printf("%lld
",min(m,(n+(m-1)*y)/x));
    }
    return 0;
} 
x换1,这波大亏

C.Cheat Sheet

有m个可能重复的单词,笔记本最多记n个字符,每两个单词之间用一个空格隔开(会占用一个字符的位置),问最多记几个不一样的单词。

将单词按照长度,再按字典序排序,然后模拟即可。

这里发现个坑爹的地方,n=-1的时候,-1>=s[i].size()居然是true,不知道是为什么,希望有道友指点一下。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s[1011];
bool cmp(string a,string b){
    return a.size()==b.size() ? a<b : a.size()<b.size();
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++) cin>>s[i];
    sort(s,s+m,cmp);
    int ans=0;
    for(int i=0;i<m;i++){
        if(i&&s[i]==s[i-1]) continue;
        if(n>=s[i].size()){
            ans++;
            n-=s[i].size();
        }else break;
        n--;
        if(n<=0) break;
    }
    printf("%d
",ans);
    return 0;
}
不,我不想记单词

H.Hay Mower 

(这表情包我喜欢艹艹艹艹艹艹)

 有n*m的草地,每个位置每秒会长aij的草,而在第t秒时,这个小熊会割掉一整行,或者一整列的草。问割了k次草后,小熊一共割了多少草。

一开始的想法复杂,但觉得可行也就直接敲了,没有往更简单的地方想。

我第一想法就是,时间从后往前,每一行每一列只会被割一次,那标记一下这一行和这一列有没有被割过就好。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=511,M=3e5+11;
const ll md=998244353;
char op[M][5];
bool visx[N],visy[N];
int pos[M];
ll a[N][N],xsum[N],ysum[N],tt[M];
int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%lld",&a[i][j]);
            a[i][j]%=md;
            xsum[i]+=a[i][j];
            if(xsum[i]>=md) xsum[i]-=md;
            ysum[j]+=a[i][j];
            if(ysum[j]>=md) ysum[j]-=md;
        }
    for(int i=0;i<k;i++) scanf("%s%d%lld",op[i],&pos[i],&tt[i]);
    ll ans=0;
    for(int i=k-1;i>=0;i--){
        tt[i]%=md;
        if(op[i][0]=='r'){
            if(visx[pos[i]]) continue;
            visx[pos[i]]=true;
            ans+=xsum[pos[i]]*tt[i]%md;
            xsum[pos[i]]=0;
            if(ans>=md) ans-=md;
            for(int j=1;j<=m;j++){
                if(visy[j]) continue;
                ysum[j]-=a[pos[i]][j];
                if(ysum[j]<0) ysum[j]=(ysum[j]%md+md)%md;
                if(ysum[j]==0) visy[j]=true;
            }
        }else{
            if(visy[pos[i]]) continue;
            visy[pos[i]]=true;
            ans+=ysum[pos[i]]*tt[i]%md;
            ysum[pos[i]]=0;
            if(ans>=md) ans-=md;
            for(int j=1;j<=n;j++){
                if(visx[j]) continue;
                xsum[j]-=a[j][pos[i]];
                if(xsum[j]<0) xsum[j]=(xsum[j]%md+md)%md;
                if(xsum[j]==0) visx[j]=true;
            }
        }
    }
    printf("%lld
",ans);
    return 0;
}
复杂的艹

但由行列就可想到,某个位置,假如3秒被割了一次,5秒又被割了一次,那么直接视为5秒时割的就好。

有个坑就是,不能先对时间取模,先比了大小先再取模。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=511;
const ll md=998244353;
ll a[N][N],r[N],c[N];
int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%lld",&a[i][j]);
            a[i][j]%=md;
        }
    char op[3];
    int pos;
    ll tt; 
    for(int i=0;i<k;i++){
        scanf("%s%d%lld",op,&pos,&tt);
        if(op[0]=='r') r[pos]=tt;
        else c[pos]=tt;
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            ans=(ans+max(r[i],c[j])%md*a[i][j]%md)%md;
        }
    printf("%lld
",ans);
    return 0;
}
简单的艹

D.Disaster Recovery

有1~n个点第i个点的权值就是斐波那契数第i项,m条边,每条的边权是两个点权和,问最小生成树中点的最大度数。

一开始也想复杂了,还想着二分找度数,但在判断它是不是最小生成树的时候就突然醒悟。

假设有a-b,c-d这两条边其中a<b,c<d,那么fib[a]+fib[b]!=fib[c]+fib[d],也就是任意两条边的边权不会相同。

因为假设b>=d+1,那么fib[b]>=fib[d]+fib[d-1],而c<=d-1,所以fib[a]+fib[b]>fib[c]+fib[d].

任意两条边的边权不会相同,那么最小生成树就只有一棵,直接求就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+11;
struct Side{
    int u,v;
    bool operator<(Side &s1)const{
        return v==s1.v ? u<s1.u : v<s1.v;
    }
}S[N*2];
int n,m,fa[N],du[N];
int find(int x){
    return fa[x]==x ? x : fa[x]=find(fa[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        scanf("%d%d",&S[i].u,&S[i].v);
        if(S[i].u>S[i].v) swap(S[i].u,S[i].v);
    }
    sort(S,S+m);
    for(int i=1;i<=n;i++) fa[i]=i,du[i]=0;
    int cnt=0,ans=0;
    for(int i=0;i<m;i++){
        int u=S[i].u,v=S[i].v;
        int fu=find(u),fv=find(v);
        if(fu==fv) continue;
        cnt++;
        fa[fu]=fv;
        du[u]++;du[v]++;
        ans=max(ans,max(du[u],du[v]));
        if(cnt==n-1) break;
    }
    printf("%d
",ans);
    return 0;
}
绿的草绿的树

L.Lottery Tickets

0~9每个数有c[i]个,问用这些数能组成的最大的4的倍数是什么。

一开始没啥思路,刚想换题,突然想起来4的倍数好像有个规律,然后就打表看了一下。

果不其然,4的倍数的规律就是2位数及以上的数最后两个数都是在某25个数之间循环的。

所以把这25个可能的最后2位数打出来,然后按照两个数的大小进行排序,让数小的在前面,然后枚举即可。

#include<bits/stdc++.h>
using namespace std;
int a[31]={
0,20,12,32,40,
24,44,52,60,16,
36,64,56,72,76,
80,28,84,48,68,
88,92,96,};
int c[11],nd[11];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        for(int i=0;i<=9;i++) scanf("%d",&c[i]);
        int flag=0;
        for(int i=0;i<23&&!flag;i++){
            int x=a[i]/10,y=a[i]%10;
            nd[x]++;nd[y]++;
            if(c[x]>=nd[x]&&c[y]>=nd[y]){
                flag=1;
                c[x]--;c[y]--;
                int pre0=1;
                for(int j=9;j>=0;j--){
                    if(c[j]){
                        if(j) pre0=0;
                        if(!j&&pre0) continue;
                        for(int k=0;k<c[j];k++) printf("%d",j);
                    }
                }
                if(x||!pre0) printf("%d",x);
                printf("%d
",y);
            }
            nd[x]--;nd[y]--;
        }
        if(!flag){
            if(c[8]) printf("8
");
            else if(c[4]) printf("4
");
            else if(c[0]) printf("0
");
            else printf("-1
");
        }
    }
    return 0;
}
0 4 8 12 16...

做了L就去吃东西了,看了一会比赛。。。然后回来傻逼的开了M,搞了快1个小时都没得。

最后看G过的多,去看了一眼,再让我360秘书帮忙翻译了一下。然后发现,啊,这波,这波是个水题。

G.Gentle Jena

题意里定义了一下函数,我就不说题意了。。。

设a[i]为以i为右边界的区间的答案,也就是题目中的A[i]=a[1]+a[2]+...+a[i]

每增加一个数bi+1,增加的区间就是[1,i+1],[2,i+1]...[i+1,i+1],

那么假设pos为bi+1左边第一个小于等于它的数的位置,那么bi+1作为最小值的区间也就是[pos+1,i+1],[pos+1,i+1]...[i+1,i+1]

而剩下的[1,i+1],[2,i+1]...[pos,i+1]由于bpos<bi+1,所以完全可以看出区间[1,pos],[2,pos]...[pos,pos]

也就是a[i]=b[i]*(i-pos)+a[pos],pos为i左边第一个小于等于b[i]的数的位置,可用单调栈求得。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e7+11;
const ll md=998244353;
stack<int> sta;
ll a[N],b[N],ans[N];
int main(){
    int n;
    ll p,x,y,z;
    scanf("%d%lld%lld%lld%lld%lld",&n,&p,&x,&y,&z,&b[1]);
    ans[1]=a[1]=b[1];
    b[0]=-1;
    sta.push(0);
    sta.push(1);
    for(int i=2;i<=n;i++){
        b[i]=(x*ans[i-1]%p+y*b[i-1]%p+z)%p;
        while(!sta.empty()&&b[sta.top()]>b[i]) sta.pop();
        int pos=sta.top();
        a[i]=(b[i]*(i-pos)%md+a[pos])%md;
        ans[i]=(ans[i-1]+a[i])%md;
        sta.push(i);
    }
    ll res=0;
    for(int i=1;i<=n;i++) res^=ans[i];
    printf("%lld
",res);
    return 0;
}
右边的区间看过来
原文地址:https://www.cnblogs.com/LMCC1108/p/12995400.html