2014-11-1 NOIP模拟赛1

时间:2022-12-17 10:16:37

冲刺NOIP2014复赛模拟题第六套第二试

 

 

题目名称

日历游戏

最大公约数

密码

英文代号

calendar

gcd

pasuwado

输入文件名

calendar.in

gcd.in

pasuwado.in

输出文件名

calendar.out

gcd.out

pasuwado.out

时限

1秒

1秒

1秒

空间限制

128M

256M

256M

测试点数目

20

10

10

测试点分值

5

10

10

是否有部分分

附加文件

题目类型

传统

传统

传统

是否有SPJ

 

 

注意:最终测试时,所有编译命令均不打开任何优化开关。

请独立完成题目,不要讨论,不得使用搜索引擎。

 

 


  1. 1.日历游戏

【问题描述】

moreD和moreD的宠物CD正在玩一个日历游戏,开始时,他们从1900年1月1日到2012年12月22日(你懂的……)选一个日期开始,依次按照如下规则之一向后跳日期:

  1. 跳到日历上的下一天。
  2. 跳到日历上的下个月的同一天(如果不存在,则不能这么做)。

要是谁正好到达2012年12月22日那么他就赢了,如果到达这天之后的日期那他就输了——原因你也懂的。

每次都是moreD先走的。

现在,给你一个日期,请问moreD一定能赢吗?

 

【输入】

输入共T行,每行三个整数,Y、M、D,分别表示年、月、日。日期在1900年1月1日到2012年12月22日之间(包含两端)。

T并不在输入数据当中。

 

【输出】

要是moreD一定能赢,输出一行YES,否则输出NO。

 

【输入输出样例一】

calendar.in

calendar.out

2012 12 20

NO

 

【输入输出样例二】

calendar.in

calendar.out

2012 12 21

YES

 

【数据描述】

    对于50%的数据,是1949年1月1日后的日期。 T <= 5

对于100%的数据,是1900年1月1日后的日期。T <= 10

2014-11-1 NOIP模拟赛12014-11-1 NOIP模拟赛1
/*
结束局面是必败态。 
必胜态可以走到必败态。 
必败态只能走到必胜态。 
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int T;
int a[12]={31,28,31,30,31,30,31,31,30,31,30,31};
int f[3005][15][40];
bool lea(int x){
    if(x%400==0||(x%4==0&&x%100!=0))return 1;
    return 0;
}
bool jud(int x,int y,int z){//判断x,y,z这天是否存在 
    if(x>2012||y>12||z>31)return 0;
    if(x==2012&&y==12&&z>=23)return 0;
    if(y==2){
        if(z>a[1]+lea(x))return 0;
    }
    else if(z>a[y-1])return 0;
    return 1;
}
int dp(int x,int y,int z){
    if(x==2012&&y==12&&z==22)return 0;//必败态 
    if(f[x][y][z]!=-1)return f[x][y][z];
    int sg[3];memset(sg,0,sizeof(sg));
    int tx=x,ty=y,tz=z,lim=a[y-1];//年月日以及这一个月的最大天数 
    tz++;//加一天 
    if(y==2&&lea(tx))lim++;
    if(tz==lim+1)tz=1,ty++;
    if(ty==13)tx++,ty=1;
    if(jud(tx,ty,tz))sg[dp(tx,ty,tz)]=1;
    tx=x,ty=y,tz=z;
    ty++;
    if(ty==13)tx++,ty=1;
    if(jud(tx,ty,tz))sg[dp(tx,ty,tz)]=1;
    for(int i=0;i<3;i++)
        if(!sg[i])
            return f[x][y][z]=i;
}
int main()
{
    freopen("calendar.in","r",stdin);
    freopen("calendar.out","w",stdout); 
    memset(f,-1,sizeof(f));
    int x,y,z;
    while(scanf("%d%d%d",&x,&y,&z)!=EOF)
    {
        for(int i=2000;i>=1900;i--)dp(i,1,1);
        if(dp(x,y,z))puts("YES");
        else puts("NO");
    }
    return 0;
}
100分 SG函数

 

2.最大公约数

【问题描述】

话说CD比较欠扁,他表示在课室的日子没有教主在旁边打他的日子太寂寞了,所以这一晚,他终于来到了电脑室被打。由于CD是大家的宠物,于是大家都来打CD了。电脑室里有n个人,第i个人希望打CD ai下。但是太多人打CD,他又会不爽,于是他规定只能有K个人打到他,并且为了公平起见,最终K个人打他的次数都必须是相同的,CD规定这个次数就是这K个人希望打他的次数的最大公约数。为什么是最大公约数呢?因为他觉得被打的次数是GCD的话他才会变成Glad CD。之前说了,CD比较欠扁,于是CD希望,K个人打他的次数的和最大。你能告诉他他最后总共会被打多少下么?

【输入格式】

第一行两个正整数n,k。

第二行n个正整数,表示每个人希望打CD多少下。

【输出格式】

输出一个正整数表示CD会被打多少下。

【样例输入输出】

gcd.in

gcd.out

3 1

1 2 3

 

3

 

【数据说明】

对于30%的数据,保证k≤n≤20。

对于50%的数据,保证输入中所有数小于5000。

对于100%的数据,保证输入中所有数小于500000,k≤n。

2014-11-1 NOIP模拟赛12014-11-1 NOIP模拟赛1
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 500010
int n,k,a[maxn],ans;
void dfs(int pos,int g,int cnt){
    if(cnt==k){ans=max(ans,g);return;}
    if(pos>n)return;
    if(g<=ans)return;
    if(k-cnt>n-pos+1)return;
    int gnow=__gcd(g,a[pos]);
    dfs(pos+1,g,cnt);
    dfs(pos+1,gnow,cnt+1);
}
int main(){
    freopen("gcd.in","r",stdin);
    freopen("gcd.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)dfs(i+1,a[i],1);
    ans*=k;
    printf("%d",ans);
}
30分 暴力
2014-11-1 NOIP模拟赛12014-11-1 NOIP模拟赛1
/*
    先开50W个桶存下每个数出现了几次,然后枚举最后的答案gcd,然后再暴力枚举所有它的倍数,看出现次数是否大于等于k就可以了。
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#define ll long long
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,K;
int f[500005];
bool jud(int x){
    int sum=0;
    for(int i=1;x*i<=500000;i++)
    sum+=f[x*i];
    if(sum>=K)return 1;
    return 0;
}
int main(){
    freopen("gcd.in","r",stdin);
    freopen("gcd.out","w",stdout);
    n=read();K=read();
    for(int i=1;i<=n;i++){
        int x=read();
        f[x]++;
    }
    for(int i=500000;i;i--){
        if(jud(i)){
            printf("%I64d\n",(ll)i*K);
            return 0;
        }
    }
    return 0;
}
100分 官方做法

 

 

3.密码

【问题描述】

    哪里有压迫,哪里就有反抗。

    moreD的宠物在法庭的帮助下终于反抗了。作为一只聪明的宠物,他打算把魔法使moreD的魔法书盗去,夺取moreD的魔法能力。但moreD怎么会让自己的魔法书轻易地被盗取?moreD在魔法书上设置了一个密码锁,密码锁上有一个问题。

    施以斯卧铺魔法吧,你有M次机会,如此将得完美密码。

    然后是一串小写字母串。

    moreD的宠物斯卧铺魔法就是施法时的字符串其中相邻两位交换。

    而moreD对于完美密码的定义自然是最小字典序了。

    请帮助moreD的宠物,想出密码吧。

【输入格式】

    第一行一个整数M,表示操作次数。

    第二行一串小写字母组成的字符串S,如题目所示。

【输出格式】

    输出完美密码。

【输入样例】

    3

       dcba

【输出样例】

    adcb

【数据范围】

    对于30%的数据|S|≤10

对于60%的数据|S|≤3,000

    对于100%的数据8≤|S|≤100,000 M≤(|S|-8)^2+2

【后记】

    宠物最终战胜了moreD,和自己的宠物快乐地生活着。

【样例解释】

    先对第3,4两位施法,字符串变成dcab,然后对第2,3两位施法,字符串变成dacb,最后对第1,2两位施法,字符串变成adcb。

2014-11-1 NOIP模拟赛12014-11-1 NOIP模拟赛1
/*
    每个字母目前最前是哪一个,可以用链表维护。
    另外目前需要的操作数可以就等于目标字母所在位置之前有多少个剩余的没有用的字母,可以用树状数组维护。
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
using namespace std; 
typedef long long ll;
ll M; 
int len; 
int C[100005],next[100005],head[26]; 
char S[100005]; 
inline void add(int x,int pos){
    next[pos]=head[x]; head[x]=pos; 
}
inline void update(int x,int v){
    for(int i=x;i<=len;i+=i&-i) C[i]+=v; 
} 
inline int query(int x){
    if(x==0) return 0; 
    int res=0; 
    for(int i=x;i>0;i-=i&-i) res+=C[i]; 
    return res; 
}
int main(){
    freopen("pasuwado.in","r",stdin);
    freopen("pasuwado.out","w",stdout);
    scanf("%lld",&M); 
    scanf("%s",S+1); 
    len=strlen(S+1); 
    for(int i=len;i>=1;i--) add(S[i]-'a',i); 
    for(int i=1;i<=len;i++) update(i,1); 
    for(int i=0;i<len;i++) {
        int j; 
        for(j=0;j<26;j++) {
            int tmp; 
            if(head[j]!=0&& (tmp=query(head[j]-1))<=M){
                M-=tmp; 
                update(head[j],-1); 
                head[j]=next[head[j]]; 
                break; 
            }
        }
        printf("%c",j+'a'); 
    }
    return 0; 
}
100分 链表+树状数组