2021牛客寒假算法基础集训营3

A 模数的世界

题意

3000ms

思路

菊苣的题解

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,i,j,k,t;
    scanf("%d",&t);
    while(t--){
        int a,b,p;
        scanf("%d%d%d",&a,&b,&p);
        if(a==0&&b==0)
            printf("0 0 0\n");
        else{
            long long x=(p-1-a)*p+a,y=(p-1-b)*p+b;
            while(__gcd(x,y)!=p-1)x+=p*p-p;
            printf("%d %lld %lld\n",p-1,x,y);
        }
    }
}

B 重力坠击

题意

在一个班级里有n个同学

每个人在考试后会得到一个A,B,C,D,E的等级和一个分数,学校要求得到等级A的人不超过k个。

已知第i个同学在得到五个等级时预期的分数分别为ai,bi,ci,di,ei

请问在最理想的情况且不违反上述条件的情况下,他们的预期分数最大值和最小值之差最小为多少。

思路

优先队列,贪心模拟,先全是E,然后取最小的值,变成D,然后一个个模拟,直到k==0或者全部变成A,过程中必定有最小之差

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e5+10;
const int modd=1e9;
struct node{
    int id,pos,val;
    node(int id,int pos,int val){this->id=id,this->pos=pos,this->val=val;}
    friend bool operator<(const node a,const node b){
        if(a.val==b.val) return a.pos>b.pos;
        return a.val>b.val;
    }
};
int a[N][5];
int main(){
    IOS;
    int n,k;
    priority_queue<node>q;
    cin>>n>>k;
    int ma=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<5;j++){
            cin>>a[i][j];
        }
        node t(i,4,a[i][4]);
        ma=max(a[i][4],ma);
        q.push(t);
    }
    int res=modd;
    while(1){
        node t=q.top();q.pop();
        if(t.pos==0)break;
        node tt(t.id,t.pos-1,a[t.id][t.pos-1]);
        q.push(tt);
        ma=max(ma,a[t.id][t.pos-1]);
        if(tt.pos==0)k--;
        if(k<0)break;
        res=min(res,ma-q.top().val);
    }
    cout<<res<<endl;
    return 0;
}

C 重力坠击

题意

在一个二维平面上有n(小于等于10)个敌人,第i个敌人可以描述为一个以(xi,yi)(abs(xi or yi)<=7)为圆心,ri(ri<=7)为半径的圆。

你每次可以对一个半径为R(R<=7)的圆范围内进行攻击(圆心自选,但圆心的横纵坐标必须为整数),对于与你攻击范围有交点的敌人都会被消灭。

你总共可以发动K次攻击,问最多能消灭多少敌人。

思路

范围都很小,可以直接暴力来,我用了状压dp来写,奈何语文理解不够,以为圈内的不算敌人数,wa了6发……然后问队友哪错了,默默地来一句,呼啦圈攻击是吧

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
const int modd=1e9;
int x[15],y[15],r[15];
int dp[3000][3];
int n,K,R;
int mp[900];
int pan(int xx,int yy,int x2,int y2,int r2){
    int zhi=(R+r2)*(R+r2);
    int zhi2=(R-r2)*(R-r2);
    int z=(xx-x2)*(xx-x2)+(yy-y2)*(yy-y2);
    if(z>zhi) return 0;
    //if(z<zhi2) return 0;我比赛zz的表现
    return 1;
}
int solve(int xx,int yy){
    int ans=0;
    for(int i=0;i<n;i++){
        if(pan(xx,yy,x[i],y[i],r[i])){
            ans+=(1<<i);
        }
    }
    return ans;
}
int main(){
    scanf("%d%d%d",&n,&K,&R);
    for(int i=0;i<n;i++){
        scanf("%d%d%d",&x[i],&y[i],&r[i]);
    }
    int tot=0;
    for(int i=-14;i<=14;i++){
        for(int j=-14;j<=14;j++){
            mp[tot++]=solve(i,j);
        }
    }
    dp[0][0]=1;
    for(int k=1;k<=K;k++){
        for(int j=0;j<tot;j++){
            for(int i=(1<<n)-1;i>=0;i--){
                if(i^mp[j] && dp[i][k-1]){
                    dp[i|mp[j]][k]=dp[i][k-1];
                }
                else if(dp[i][k-1]){
                    dp[i][k]=dp[i][k-1];
                }
            }
        }
    }
    int mx=1;
    for(int i=0;i<=(1<<n)-1;i++){
         if(dp[i][K]){
            int k=i,ge=0;
            while(k){
                if(k&1)ge++;
                k>>=1;
            }
            mx=max(mx,ge);
        }
    }
    printf("%d\n",mx);
    return 0;
}
/*
2 2 1
0 0 1
0 0 1
*/

D Happy New Year!

签到题

反思为什么会wa3次

读题不仔细

E 买礼物

题意

在卖礼物的超市中有n个柜子,每个柜子里都摆放了一个礼物,每个礼物有自己的一个编号,第i个柜子里的礼物编号为ai。

茶山牛想给牛牛和牛妹买相同编号的礼物,但礼物有可能在某个时刻被其他人买走,而且柜子数量太多,因此茶山牛在某个时刻只想知道某一个柜子区间是否能买到两件相同编号的礼物。

具体来说,有q次操作,格式如下:

1 x,第x个柜子里的礼物被买走,保证此时这个柜子里的礼物还在。

2 l r,茶山牛询问第l到第r个柜子未被买走的礼物中是否有两个礼物编号相同。

思路

题解菊苣的思路,然后我理解完写了一边orz

记录每一个数字后面第一个等于它的数字的位置,查询区间L和R的最小值是否小于R,如果小于等于R则有相同数

因为有操作1,删除某个值,所以需要用链表维护,这个数字前面第一个等于它的数字位置和后面第一个等于它的数字的位置。

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 5e5+10;
const int modd=1e9;
int tree[N<<2],a[N],pos[N],qian[N];
int n,q;
void pushup(int rt){
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
void build(int l,int r,int rt){
    if(r==l){
        tree[rt]=pos[l];
        return;
    }
    int mid=(r+l)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int rt,int x,int zhi){
    if(l>x || r<x) return;
    if(l==r && r==x){
        tree[rt]=zhi;
        return ;
    }
    int mid=(r+l)>>1;
    update(l,mid,rt<<1,x,zhi);
    update(mid+1,r,rt<<1|1,x,zhi);
    pushup(rt);
}
int querry(int l,int r,int rt,int L,int R){
    if(r<L || l>R)return n+1;
    if(r<=R && l>=L){
     //   cout<<tree[rt]<<" "<<l<<" "<<r<<endl;
        return tree[rt];
    }
    int mid=(r+l)>>1;
    return min(querry(l,mid,rt<<1,L,R),querry(mid+1,r,rt<<1|1,L,R));
}
int main(){
    IOS;
    cin>>n>>q;
    map<int,int>mp;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(mp[a[i]]==0){
            mp[a[i]]=i;
        }
        else{
            qian[i]=mp[a[i]];
            pos[qian[i]]=i;
            mp[a[i]]=i;
        }
    }
    for(int i=1;i<=n;i++){
        if(pos[i]==0)pos[i]=n+1;
    }
    build(1,n,1);
    for(int i=0;i<q;i++){
        int c;cin>>c;
        if(c==1){
            int x;cin>>x;
            update(1,n,1,qian[x],pos[x]);
            pos[qian[x]]=pos[x];
            qian[pos[x]]=qian[x];
            pos[x]=n+1;qian[x]=0;
            update(1,n,1,x,n+1);
        }
        else{
            int L,R;
            cin>>L>>R;
           // cout<<querry(1,n,1,L,R)<<endl;
            if(querry(1,n,1,L,R)<=R){
                cout<<"1"<<endl;
            }
            else{
                cout<<"0"<<endl;
            }
        }
       /* for(int j=1;j<=n;j++){
            cout<<qian[j]<<" ";
        }cout<<endl;
        for(int j=1;j<=n;j++){
            cout<<pos[j]<<" ";
        }cout<<endl;*/
    }
    return 0;
}
/*
5 5
1 2 1 2 1
2 2 4
2 2 5
1 2
2 2 4
2 2 5
*/

第一次看到一半题解就理解了,然后独立完成的线段树……但问题是比赛时候想不到这种方法,看来思维思考能力的差距orz

F 匹配串

题意

一个模式串指仅包含小写英文字母和至少一个'#'的字符串,其中'#'可以匹配一段任意长度的任意小写字母字符串。

一个匹配串指待匹配的只包含小写字母的字符串。

一个模式串和一个匹配串相匹配当且仅当把模式串里面的'#'全部分别替换成空或一段小写字母字符串后,两个串完全相同。

现在给出n个模式串,问有多少不同的匹配串与这些模式串全部相匹配。

如果答案有无穷多个,输出-1。

思路(赛后第二遍测我的代码发现wa了,只过了百分之90.1)

要么0个,要么-1

每个串的第一个#之前的和最后一个#之后的会影响结果

然后我发现我题目读错了,原来是全部相匹配,我没看见全部二字,框框框的写代码,我悔啊……

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 1000005;
 
char s[N];
int main() {
    int n;
    scanf("%d", &n);
    int ans = -1;
    string t1, t2;
    for (int i = 0; i < n; i++) {
        scanf("%s", s);
        int len = strlen(s);
        for (int i = 0; s[i] != '#'; i++) {
            if (i >= t1.size()) {
                t1.push_back(s[i]);
            } else {
                if (t1[i] != s[i]) ans = 0;
            }
        }
        reverse(s, s + len);
        for (int i = 0; s[i] != '#'; i++) {
            if (i >= t2.size()) {
                t2.push_back(s[i]);
            } else {
                if (t2[i] != s[i]) ans = 0;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
/*
4
abc#a
abf#a
#a
#a
0
*/

G 糖果

题意

在一个幼儿园里面有\mathit nn个小朋友,分别编号1,2,...,n。在这些小朋友中有一些小朋友互为朋友关系,总共有m对朋友。

作为幼儿园老师,你想买一些糖果分给小朋友,你知道第i个小朋友想要至少a[i]个糖果,否则他就会不开心。

同时,如果一个小朋友得到的糖果数小于他某个朋友得到的糖果数,他也会不开心。

请问你最少买多少糖果才能保证每个小朋友都不会不开心呢?

思路

一看就是并查集找个数,然后找集团最大值,比赛中的提示误人,幸好直接忽略了,因为好像标记标反了wa了一发

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
const int modd=1e9;
int f[N],a[N],ge[N];
int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
void init(){
    for(int i=0;i<N;i++){
        f[i]=i;ge[i]=1;
    }
}
int main(){
    init();
    int n,m;
    ll ans=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=0;i<m;i++){
        int u,v;scanf("%d%d",&u,&v);
        int uu=F(u),vv=F(v);
        if(uu!=vv){
            f[uu]=vv;
            ge[vv]+=ge[uu];
            a[uu]=max(a[uu],a[vv]);
            a[vv]=a[uu];
        }
    }
    for(int i=1;i<=n;i++){
        if(f[i]==i){
            ans+=(ll)a[i]*ge[i];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

H 数字串

题意

牛牛发现了一种方法可以将只包含小写字母的字符串按照以下方式使其转换成一个数字串:

取其中的每个字母,a转换为1,b转换为2......z转换为26,然后将这些数字拼接起来。

例如,abcz可以转换为12326。

现在给出一个只包含小写字母的字符串S,你需要找到一个只包含小写字母的字符串T,使得两个串不相同但是能转换成相同的数字串。

思路

模拟吧,找a后面是否有ai,b后面是否有af ,第10个和第20个拆不了,其他两位数都可以拆(感觉唯一坑点)

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
const int modd=1e9;
char s[N],ss[N*2];
int main(){
    scanf("%s",s);
    int l=strlen(s);
    int flag=0,tot=0;
    for(int i=1;i<l;i++){
        if(flag){
            ss[tot++]=s[i];
        }
        else if(s[i-1]=='a' && s[i]<='i'){
            ss[tot++]=10+s[i];flag=1;
        }
        else if(s[i-1]=='b' && s[i]<='f'){
            ss[tot++]=20+s[i];flag=1;
        }
        else if(s[i-1]>'j' && s[i-1]<'t'){
            ss[tot++]='a';ss[tot++]=s[i-1]-10;ss[tot++]=s[i];flag=1;
        }
        else if(s[i-1]>'t'){
            ss[tot++]='b';ss[tot++]=s[i-1]-20;ss[tot++]=s[i];flag=1;
        }
        else{
            ss[tot++]=s[i-1];
        }
    }
    if(flag){
        for(int i=0;i<tot;i++){
            printf("%c",ss[i]);
        }printf("\n");
    }
    else{
        printf("-1\n");
    }

    return 0;
}

I 序列的美观度

题意

求一个数组a删掉某几个数字,是否能组成多少对a[i]=a[i+1] ,求最多

思路

很快想到了map,然后wa百思不得其解,直到友好的比赛方发了第二个样例

7
1 1 2 2 2 1 1
//答案是4 1 1,2 2,2 2,1 1 

然后修改了一下就过了

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
const int modd=1e9;
int a[N];
int main(){
    unordered_map<int,int>mp;
    int n;
    scanf("%d",&n);
    int ge=0;
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        if(mp[a[i]]==1){
            ge++;mp.clear();
            mp[a[i]]=1;
        }
        else{
            mp[a[i]]++;
        }
    }
    printf("%d\n",ge);
    return 0;
}

J 加法和乘法

题意

有一天牛牛和牛妹在做游戏,规则如下:

桌面上摆着n张纸牌,每张纸牌上写着一个正整数,由牛牛先手轮流执行以下操作:

1.如果桌面上只剩一张纸牌,游戏结束,这张纸牌上的数字如果是奇数则牛牛胜利,反之牛妹胜利。

2.当前行动玩家选择两张纸牌,设上面的数字分别为X,Y,接下来玩家从加法和乘法中选择一个并应用到这两个数字上,得到结果为Z,接下来将选择的两张纸牌丢弃,并拿一张新的纸牌
放到桌面上,在上面写上Z。

假设双方均以最优策略行动,最后谁会赢?

思路

统计奇偶个数,循环一边,是牛牛就尽量把偶数消掉,牛妹的话,尽量增加偶数,模拟循环过程……反正也就n-1次即可

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
const int modd=1e9;
int a;
int main(){
    int n;
    scanf("%d",&n);
    int ji=0,ou=0;
    for(int i=0;i<n;i++){
        scanf("%d",&a);
        if(a&1){
            ji++;
        }
        else{
            ou++;
        }
    }
    int flag=0;
    for(int i=1;i<n;i++){
        if(i&1){
            if(ji==0){
                flag=1;break;
            }
            if(ou==0){
                ji--;
            }
            else{
                ou--;
            }
        }
        else{
            if(ji==0){
                flag=1;break;
            }
            if(ji>=2){
                ji-=2;ou++;
            }
            else{
                ji--;
            }
        }
    }
    if(!flag && ou==0){
        printf("NiuNiu\n");
    }
    else{
        printf("NiuMei\n");
    }
    return 0;
}
posted @ 2021-02-08 21:29  ouluy  阅读(109)  评论(0编辑  收藏  举报