【NOIP2017】SummerTraining0726

来源:互联网 发布:baocms7.6源码下载 编辑:程序博客网 时间:2024/06/05 19:41

T1结论题,T2贪心,T3优化暴力,这套题目很简单,但是T3的优化有点难想到。
100+100+0=200 rank18。
PS:一个梗:考完后myx大佬说你们是不是有人在用手机查T3题解?我博客题解的访问量暴涨20,233。

T1

第1题 谜题
首先请解以下谜题:车下的数字是什么?
这里写图片描述

正确的答案是87 。这道题对小龙大犇来说太轻松了,于是他想加强难度来考考你:对于给定的长度N,能否获得刚好长度为N的数列,使数列中的每个数经过翻转恰好是连续的数,如N==3时,数列 11 01 60 是合法的。数字的翻转符合以下规定:1、0、8翻转后是其本身;6和9翻转后互相转变;其他数字翻转后不合法;一位数将默认有前导零;只考虑一位或两位正整数。
【输入】puzzle.in
一行一个数字N(N<=99)
【输出】puzzle.out
如果能,输出“YES”;否则输出“XLSB”;
【样例输入】
3
【样例输出】
YES
【数据规模】
对于10%的数据,N<=1;
对于20%的数据,N<=3;
对于50%的数据,N<=4;
对于100%的数据1<=N<=99;

Solution

  1. 手推或打代码爆搜发现最长为 8 9 10 11 所以<=4输出YES。

CODE

#include<bits/stdc++.h>using namespace std;int main(){    int n;    cin>>n;    if (n>4) printf("XLSB\n");    else printf("YES\n");    return 0;}

T2

第2题 选修课
温州中学开放了许多选修课,每节选修课都属于一种种类。精力旺盛的黄小龙同学想要尽可能多的参加选修课,但是他只能选择M种种类的课程。当然,对于他所选的种类,他会去上所有该种类的课。现在他想知道他最多能上几节选修课,以及上最多选修课的方案数。
两种方案被认为不同当且仅当一种方案中存在另一种方案所没有的选修课。
【输入】course.in
第一行一个只由小写字母组成,长度为N的字符串。表示有N节选修课,以及每节选修课的种类。
第二行一个正整数M。
【输出】course.out
输出一行,两个用空格隔开的正整数,分别表示最多选修课数量和方案数。
【样例输入1】
abcde
1
【样例输出1】
1 5
【样例输入2】
ababac
2
【样例输出2】
5 1
【数据规模】
对于30%的数据,M==1;
对于另外30%的数据,每种种类的选修课最多只有一节;
对于100%的数据1<=M<=26、1<=N<=100000;

Solution

  1. 优先课程数最多的种类,可改变的是已选种类中课程数最少的那一类,A=课程数最少的种类选了几种,B=与该课程数相等的种类总共有多少,方案数为。可以考虑高精度乘法与除法,避开的方法:考虑到最终答案最大为并不会超longlong,但中间过程会。以素数乘积形式表示,在数组中加加减减,最后乘起来2、边乘边除。(标程用了第一种,但其中的高精度是没必要的)
  2. PS:其实中间过程不会超long long ,只需要判断m是否大于n/2即可。

CODE

#include<bits/stdc++.h>using namespace std;#define ll long longconst int MAXN=100005;int a[100],b[MAXN],n,m,cnt,ans;char str[MAXN];bool cmp(int x,int y){    return x>y;}ll getC(int n,int m){    ll x,y;    x=y=1;    if (m>n/2) m=n-m;    for (int i=m;i>=1;i--)    {        x*=(ll)i+n-m;        y*=(ll)i;    }    return x/y;}int main(){    scanf("%s",str);    scanf("%d",&m);    n=strlen(str);    b[0]=26;    for (int i=0;i<n;i++)     {        int t=a[str[i]-'a']++;        b[t]--; b[t+1]++;    }    sort(a,a+26,cmp);    for (int i=0;i<m;i++)     {        ans+=a[i];        if (a[i]==a[m-1]) cnt++;    }    printf("%d %lld\n",ans,getC(b[a[m-1]],cnt));        return 0;}

T3

第3题 质数
宿管有一套神奇的控制系统来控制寝室的灯的开关:
这里写图片描述
共有N盏灯,标号为1到N,有M个标有不同质数的开关,开关可以控制所有标号为其标号倍数的灯,按一次开关,所有其控制的灭着的灯都点亮,所有其控制的亮着的灯将熄灭。现在,宿管可以无限的按所有开关,所有灯初始状态为熄灭,请求出最多能点亮几盏灯。
【输入】
输入有多组数据,第一行一个正整数T表示数据组数。
每组数据第一行两个整数N,M。
第二行M个不同的质数表示开关上的标号,保证所有标号<=N。
【输出】
对于每组数据输出一行一个整数表示最多亮灯数。
【样例输入】
4
10 2
2 5
21 4
2 3 5 7
100 1
5
100 3
3 19 7
【样例输出】
5
11
20
42
【数据范围】
对于50%的数据,N<=15;
对于100%的数据,T<=10,N<=1000。

Solution

  1. 将质数根据根号N划分成两部分,大于根号N部分不会互相影响,小于根号N素数最多11个,穷举小于根号N的开关的开关情况,每种情况贪心判断大于根号N部分。
    (关键)为什么这样分就可以呢? 因为后半部分不会 互相影响 所以可以贪心判断。
    这题我调了好久,最后发现是状压后没有判是不是1,即少了if (i && 1 << j-1)这句话

CODE

#include<bits/stdc++.h>using namespace std;int p[1005],n,m,ans,k;bool f[1005];int main(){    int T;    cin>>T;    while (T--)    {        memset(p,0,sizeof(p));        ans=0; k=0;        scanf("%d%d",&n,&m);        for (int i=1;i<=m;i++)            scanf("%d",&p[i]);        sort(p+1,p+1+m);        for (int i=1;i<=m;i++)            if (p[i]*p[i]<=n) k=i;        for (int i=0;i<1<<k;i++)        {            int tot=0;            memset(f,false,sizeof(f));            for (int j=1;j<=k;j++)                if (i & (1<<j-1))                    for (int x=p[j];x<=n;x+=p[j])                        f[x]=!f[x];            for (int j=1;j<=n;j++)                 if (f[j]) tot++;            for (int j=k+1;j<=m;j++)            {                int cnt=0;                for (int x=p[j];x<=n;x+=p[j])                    if (f[x]) cnt--; else cnt++;                if (cnt>0)  tot+=cnt;            }            ans=max(ans,tot);        }        cout<<ans<<endl;    }    return 0;}
原创粉丝点击