SNNU2017校赛(部分)题解

博主在SNNU2017校赛中遇到热身赛B题,起初使用map记录数字频率,但初次尝试未能通过。与西电选手交流后,了解到使用iterator遍历map可以解决。SNNU选手提示异或运算的性质,输入时与0异或即可得到答案,但博主仍困惑于初试为何错误。有学长建议map可能不如set有效,并告诫尽量避免过度依赖STL,因为下午的A题因STL使用不当导致连续错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.热身赛B题:

一开始,就用map存了下每个数字出现的次数,然后扫一遍,碰见1就输出(WA的不知所措)中午,跟西电大佬吃饭的时候,发现他也是用的map,然后iterator扫一遍过了,用队列的我表示从来没这么玩过(大佬下午就全场第三orz);然后SNNU大佬告诉我标程。。xor的逆运算是xor,输入的时候和0异或就好,最后剩下的就是答案,我竟然忘了(ys我对不起你),但是依然不知道,第一发为什么错。。有个学长说他用map也没过,换set就好了。。。(ys告诉我这水题,尽量别用stl。。。然后下午A题崩了用了一堆stl无限WA)

2.正式赛(引用大佬的)

A:开个数组score[i]保存A-Z的分数,最后输出最高的,注意分数相同字典序,(如此水题我竟然1个半小时卡了。。全场过的人辣么多,死在热身赛的stl上了,水题水做)

C:三进制计算器:3进制阶乘统计末尾0,现场·我是打表3^k,然后可以整除就加对应的k也算是过了,不过标程很强
证明:一个数N!=M*3^k,k就是0的个数,易知,当K最大时M3互质,所以我们只要分离出N!中所有的3即可。也就是说其实计算N! = m*3^kK的最大值,只要不断将n/3的结果加到K中去就好,每次执行一次操作n=n/3.显示意义就是计算连续的N个数中3的倍数,9的倍数,27的倍数......所有之和,同时在每次计算时3i次方被恰好会被计算i次,从而保证程序的正确性。
#include <iostream>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cstdlib>
 
using namespace std;
 
int main()
{
    int t;
    scanf("%d",&t);
    long long n;
    while(t--)
    {
        long long sum=0;
        scanf("%lld",&n);
        while(n!=0)
        {
            sum+=n/3;
            n=n/3;
        }
        printf("%lld\n",sum);
    }
    return 0;
}

D:题目要求N个点中距离距离最近的两个点的距离,看似是一道平面上N个点求最短距离的KD树题目(我就是这么想的,一开始果断放),但是仔细观察题目中所给的距离D的定义D = |(X2-X1)+(Y2-Y1)| 可做如下变形:D = |(x2+y2)-(x1+y1)|;(最后,好像10几分钟的时候,瞟了两眼,想死啊,幸好发现了)

E:这题弱连输入都不会,感觉gets()有问题,下来学习了一下,学到了新函数,我这string类和输入输出看样子得大补;
解法:每次如果行末有";"就增加缩进,有空行就减少缩进;比赛的时候,好像因为生成数据的系统和评测机的不一样,要在行末多加个空行。
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
string s;
int tab;
int main()
{
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    tab=0;
    bool flag=0;
    while(getline(cin,s))//getline
    {
        if(s.empty())
        {
            for(int i=0;i<tab;i++)
            cout<<" ";
            cout<<endl;
            tab-=4;
            continue;
        }
        if(s[s.length()-1]==':')
            flag=1;
        for(int i=0;i<s.length();i++)
        {
            if(s[i]==',')
            {
                s.insert(i+1," ");
                i++;
            }
        }
        for(int i=0;i<tab;i++)
            cout<<" ";
        cout<<s<<endl;
        if(flag)
        {
            tab+=4;
            flag=0;
        }
    }
    return 0;
}
F:这类状态压缩问题之前一直没做过,一开始的思路是:dp[i][3][3][3][3][3][3][3][3][3][3][m]表示状态i指的是前i件物品,后面的10维0表示偶数,1表示奇数,2表示这一维未被使用,m表示魅力值,显然魅力m<=100000,加上前面的维,数组开不了。然后,又想可以常规降维
dp[i][3][3][3][3][3][3][3][3][3][3]=m;这显然也挺麻烦的,最后看了标程,dp[i][W]=m,W是用2进制的形式存下当前状态,解决了维度不确定的问题,还有数组开不下的问题,最强的是又利用了一次xor的性质,
W=W^X,每次xor表示这一位出现次数的奇偶,相当于将01背包中的体积转化为奇偶状态,dp[0][0]=0;其余为-1,表示不能直接到达
#include<bits/stdc++.h>

using namespace std;

int bag[1003][1111];

int main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    int T;
    cin>>T;
    while(T--)
    {

        int n,m;
        cin>>n>>m;
        int goal=1<<m;
        memset(bag,-1,sizeof(bag));
        bag[0][0]=0;

        for(int i=1 ; i<=n ; i++)
        {
            int q,s;
            cin>>q>>s;
            int w=0;
            for(int j=0; j<s; j++)
            {
                int c;
                cin>>c;
                w=w|(1<<(c-1));//压缩成二进制***
            }
            for(int j=0; j<goal; j++)
            {
                if(bag[i-1][j]==-1)
                    continue;
                bag[i][j^w]=max(bag[i][j^w],bag[i-1][j]+q);
                bag[i][j]=max(bag[i][j],bag[i-1][j]);
            }
        }
        cout<<bag[n][goal-1]<<endl;

    }
    return 0;
}


H:1)对于有i个数字的状态dp[i]可以由(i-1,n-i+1)...(1,n-1),满足乘法原理。1-33打表即可。
2)卡特兰数法:dp[n]=h[n-1];h[n]=C(2n,n)/(n+1)=C(2n,n)-2C(2n,n-1)(n=0,1,2,...)
I:2进制大数加法,乘法,记下模板:
#include<bits/stdc++.h>
using namespace std;
char a[2003];
char b[2003];
char c[2003];
 
void toadd(char sa[],char sb[],int len)
{
    for(int i=0; i<len; i++)
    {
        if((sa[i]&sb[i])=='1')
        {
            for(int j=i; j<=len; j++)
            {
                if(sa[j]=='1')
                {
                    sa[j]='0';
                }
                else
                {
                    sa[j]='1';
                    break;
                }
            }
        }
        else
        {
            sa[i]=max(sa[i],sb[i]);
        }
    }
}
 
 
 
int main()
{
    int T;
    cin>>T;
    getchar();
    while(T--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        int la=0;
        int lb=0;
        char ch;
        int opr;
        while(1)
        {
            ch=getchar();
            if(ch=='*')
            {
                opr=1;
                break;
            }
            else if(ch=='+')
            {
                opr=0;
                break;
            }
            a[la++]=ch;
        }
        while(1)
        {
            ch=getchar();
            if(ch=='\n')
                break;
            b[lb++]=ch;
        }
        for(int i=0 ; i<la/2 ; i++)
        {
            int t=a[i];
            a[i]=a[la-i-1];
            a[la-i-1]=t;
        }
        for(int i=0 ; i<lb/2 ; i++)
        {
            int t=b[i];
            b[i]=b[lb-i-1];
            b[lb-i-1]=t;
        }
        if(opr==0)
        {
            toadd(a,b,max(la,lb));
        }
        else
        {
            for(int i=0 ; i<2002 ; i++)
                c[i]='0';
            for(int i=0,j=lb-1; i<lb; i++,j--)
            {
                if(b[i]=='1')
                {
                    toadd(c+i,a,la+i);
                }
            }
            strcpy(a,c);
        }
        la=0;
        for(int i=2002 ; i>0 ; i--)
        {
            if(a[i]>'0')
            {
                la=i;
                break;
            }
        }
        for(int i=la; i>=0; i--)
        {
            cout<<a[i];
        }
        cout<<endl;
    }
    return 0;
}
(难题看懂以后补上)

orz






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值