Evanyou Blog 彩带

10月9日模拟题解题报告

PS:昨天月考,极其恶劣的成绩,结果李总在伤口上给我们撒盐,今天搞了个模拟赛~~~

                                  1、期末考试

finaltest.cpp/c/pas

1s / 128M

[题目描述] 

山山同学在期末考试前向他妈妈发誓,说自己能考到年级前10,他的妈妈不相信,但承诺道每一科他如果上了90分,她就可以奖励山山同学一些money。所以山山同学发奋复习,准备拿到最多的money去买他朝思暮想的东西。期末考试成绩下来了,山山立刻去看了成绩表,结果呢……

[输入描述]

输入文件finaltest.in一共m+3行。

第一行有1个整数m,表示了有多少个考试科目(英文小写的一个单词)。

接下来的m行中每行有三个数据:

第一个是科目。

第二个是考上90能拿的钱q(可能有最多2位的小数)。

第三个是他的这科成绩s。

接下来一行是他要买的东西的名称。

接下来还有一行,表示他买一个这样的东西需要的钱e。

[输出描述]

输出文件finaltest.out有两行。

第一行输出他一共能拿到的钱t(输出两位小数)。

第二行输出他能购买的量(整数),如果一个都没法买,就输出“ni wei he na me diao!”(输出不带引号)

[样例输入]

3

chinese 10 89

math 100.12 95

english 20 85

cassiopeia

69

[样例输出] 

100.12

1

[数据范围] 

数据保证有10%的数据m<=5。

数据保证有30%的数据不带小数。

数据保证100%的数据 0<m<1000 , 0<q<=10000,0<e<=1000000。

数据保证物品名全为英文小写。

Solution:

这道题,看了就呵呵~~送分(命)题,直接跑一遍模拟,对于分数大于等于90的便把钱加上,最后输出就行了。。。额,结果测出来65分,(神奇怎么会有错误呢?),一看数据,****科目名称竟然还有空格,处理一下,AC。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 int m;double tot,e;
 8 string b;
 9 int s;string k;double q;
10 inline int gi()                         //读入优化
11 {
12     int a=0;char x=getchar();bool f=0;
13     while((x>'9'||x<'0')&&x!='-')x=getchar();
14     if(x=='-')f=1,x=getchar();
15     while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
16     return f?-a:a;
17 }
18 int main()
19 {
20     freopen("finaltest.in","r",stdin);
21     freopen("finaltest.out","w",stdout);
22     scanf("%d",&m);
23     for(int i=1;i<=m;i++){                      //累加钱
24     cin>>k;
25     scanf("%lf",&q),s=gi();
26     if(s>=90)tot+=q;
27     }
28     cin>>b>>e;
29     printf("%.2lf\n",tot);
30     if(tot<e)printf("ni wei he na me diao!");      //太diao了
31     else printf("%d",(int)(tot/e));
32     return 0;
33 }

                                  2、买票

ticket.cpp/c/pas

1s /128M

[题目描述]

方老师在电影院卖票,有一新的电影上映,每张票的票价是5元,现在城里的支票有2种面值,5元和10元。如果一个人用5元的买票,那么方老师直接收下这5元,如果一个人用10元来买票,那么方老师会找给他一张5元。

方老师一开始有K张5元的支票,但如果某一刻时刻他没有5元的支票并且来了一个人持有10元的支票,那么方老师就会被打。

现在有N个人持有5元的支票和M个人持有10元的支票来买票,这N+M人的顺序不确定,问方老师有多大概率不会被打?

[输入描述]

输入文件ticket.in

三个数N,M,K分别表示有N个人持有5元的支票,M个人持有10元的支票,方老师一开始有K张5元的支票。

[输出描述]

输出文件ticket.out

一个数,表示概率,保留6位小数。

[样例输入]

5 3 1

[样例输出]

0.857143

[数据范围]

对于20%的数据,1 <= n, m <= 100

对于40%的数据,1 <= n, m <= 1000

对于100%的数据,1 <= n, m <= 100000, 0 <= k <= 10

Solution:

卡特兰数列的变形,本题把5元看做中有1,10元看做-1,起点在(k,0)处,要走到终点(n + k,m)处,不能超越从原点出发的对角线。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N = 100110;
 5 const int INF = 1000000000;
 6 const int Mod = 1000000007;
 7 int n, m, K;
 8 int main()
 9 {
10     freopen("ticket.in","r",stdin);
11     freopen("ticket.out","w",stdout);
12     cin>>n>>m>>K;
13     double ans = 1;
14     if(n + K < m)
15         ans = 1;
16     else if(K >= m)
17         ans = 0;
18     else{
19         for(int i=0; i < K + 1; i++){
20             ans *= 1.0 * (m - i) / (n + K + 1 - i);
21         }
22     }
23     printf("%.6f\n", 1 - ans);
24     return 0;
25 }

 

 

                            3、乘方

math.cpp/c/pas

1s / 128M

[题目描述]

求 2^x % y的值,即求2的x次幂除以y的余数。

[输入描述]

输入文件math.in

一行两个整数 x 和 y,用空格隔开

[输出描述]

输出文件math.out

一行一个整数,最后的答案

[样例输入]

3 3

[样例输出] 

math.out

2

[数据范围]

对于 10%的数据: x<=2^6 – 1

对于 20%的数据: x<=2^17 – 1

对于 40%的数据: x<=2^64 – 1

对于 70%的数据: x<=2^333 – 1

对于 100%的数据: x<=2^233333 – 1 , y 为 [3,1000] 的质数

Solution:

40 分很容易,直接用 long long + 快速幂。
70分也不难,直接高精度。如果你写的高精度,但是还没得到 70 分,那就说明你没有压位(想想高精度相当于是循环
啊!)
100 分算法:费马小定理+快速幂
2^a%mod = (2^(a%(mod-1))%mod
大家可以找找规律,
令 y=3, x=0, 1, 2, 3, 4, …
令 y=5, x=0, 1, 2, 3, 4, …
令 y=7, x=0, 1, 2, 3, 4, …
令 y=11, x=0, 1, 2, 3, 4, …
……
大家可以发现余数是呈周期性变化的,周期为 y – 1
所以上面的等式就可以简单证明了(严格证明的话要用费马小定理)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 #define ll long long
 6 ll x,y,m=2,cnt,a[100005],n;
 7 inline void gi()
 8 {
 9   char i=getchar();
10   while(i<'0'||i>'9')i=getchar();
11   while(i>='0'&&i<='9'){a[++cnt]=i-'0';i=getchar();}
12   scanf("%lld",&y);
13 }
14 inline ll f(ll k)
15 {
16     ll res=1;
17     while(k)
18     {
19         if(k&1)res=res*m%y;
20         m*=m;m%=y;
21         k>>=1;
22     }
23     return res;
24 }
25 int main()
26 {
27     freopen("math.in","r",stdin);
28     freopen("math.out","w",stdout);
29     gi();
30     for(int i=1;i<=cnt;i++)
31     n=(n*10+a[i])%(y-1);
32     printf("%lld",f(n));
33     return 0;
34 }

 

4、帕秋莉•诺蕾姬

patchouli.cpp/c/pas

1s / 128M

[题目描述]

在幻想乡,帕秋莉•诺蕾姬是以宅在图书馆闻名的魔法使。这一天帕秋莉又在考虑如何加强魔法咒语的威力。帕秋莉的魔法咒语是一个仅有大写字母组成的字符串,我们考虑从’A’到’Z’分别表示0到25的数字,于是这个魔法咒语就可以看作一个26进制数。帕秋莉通过研究发现,如果一个魔法咒语所代表的数能够整除10进制数M的话,就能够发挥最大的威力。若当前的魔法咒语并不能整除M,帕秋莉只会将其中两个字符的位置交换,尽量让它能够被M整除,当然由于某些咒语比较特殊,无论怎么改变都不能达到这个目的。请你计算出她能否只交换两个字符就让当前咒语被M整除。(首位的’A’为前导0)

[输入描述]

输入文件一共2行。

第1行为1个字符串,长度不超过L。
第2行为1个正整数M。

[输出描述]

输出文件只有一行。

第1行为用空格隔开的2个整数,输出时先输位置靠前的那个。
如果存在多种交换方法,输出字典序最小的,比如1 3和1 5都可以达到目的,就输出1 3;1 3和2 4都行时也输出1 3。注意字符串下标从左到右依次为1到L开始。如果初始魔法咒语已经能够整除M,输出”0 0”;若无论如何也不能到达目的输出”-1 -1”。
[输入样例]

PATCHOULI
16

[输出样例]

4 9

[数据范围]

数据保证30%的数据:1 <= L <= 10, 1 <= M <= 100
数据保证50%的数据:除前面30%外,1 <= L <= 500, M = 5或25或26
数据保证100%的数据:1 <= L <= 2,000, 1 <= M <= 200,000

Solution:

1、乱搞出来的75分。30%的数据直接转换进制暴力码,50%的数据进行特判,去掉5或25或26的倍数什么的,就是随便弄一下就好了。先转换成一个10进制数,并且这个10进制数是小于2^63-1的。枚举每一对字母进行交换,若当前字符串所对应的10进制数能够整除M,输出即可。在算法一的基础上加上对5,25,26的特判:对于5,25只要各位数字之和能够被5或25整除,则该数能够5或25整除,否则无解;对于26,只需判断字符串内是否有’A’,无’A’则无解,否则只需把’A’换至最后一位,就能被26整除。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #define ll long long
 6 using namespace std;
 7 const int MAXL=2000+5;
 8 char L[MAXL];int l,M;
 9 int get_L()
10 {
11     int x=0;
12     for(int i=1;i<=l;i++)
13     x*=26,x+=L[i]-'A',x%=M;
14     return x%M;
15 }
16 void xfs()
17 {
18     if(!get_L()){puts("0 0");return;}
19     for(int i=1;i<l;i++)
20     {
21         for(int j=i+1;j<=l;j++)
22         {
23             swap(L[i],L[j]);
24             if(!get_L())
25             {printf("%d %d\n",i,j);return;}
26             swap(L[i],L[j]);
27         }
28     }
29     puts("-1 -1");
30     return;
31 }
32 int main()
33 {
34     freopen("patchouli.in","r",stdin);
35     freopen("patchouli.out","w",stdout);
36     scanf("%s%d",L+1,&M);
37     l=strlen(L+1);
38     if(M==26)
39     {
40         if(L[l]=='A'){puts("0 0");return 0;}
41         for(int i=1;i<=l;i++)
42         if(L[i]=='A'){printf("%d%d\n",i,l);return 0;}
43         puts("-1 -1");
44         return 0;
45     }
46     if(M==5)
47     {
48         if(L[l]-'A'%5==0)
49         {puts("0 0");return 0;}
50         for(int i=1;i<=l;i++)
51         if(L[i]-'A'%5==0){printf("%d %d\n",i,l);return 0;}
52         puts("-1 -1");
53         return 0;
54     }
55     if(M==25)
56     {
57         int ans1=10000,ans2=10000;
58         if(L[l-1]-'A'==0||L[l-1]-'A'==2||L[l-1]-'A'==5||L[l-1]-'A'==7)
59         if(L[l]-'A'==5||L-'A'==0)
60         {puts("0 0");return 0;}
61         if(L[l]-'A'==5||L[l]-'A'==0)
62         {
63             for(int i=1;i<l;i++)
64             if(L[i]-'A'==0||L[i]-'A'==2||L[i]-'A'==5||L[i]-'A'==7)
65             {ans1 = i;break;}
66         }
67         if(L[l-1]-'A'==0||L[l-1]-'A'==2||L[l-1]-'A'==5||L[l-1]-'A'==7)
68         {
69             for(int i=1;i<l-1;i++)
70             if(L[i]-'A'==0||L[i]-'A'==5)
71             {ans2=i;break;}
72         }
73         if(ans1<ans2)
74         {printf("%d %d\n",ans1,l-1);return 0;}
75         if(ans2<ans1)
76         {printf("%d %d\n",ans2,l);return 0;}
77         puts("-1 -1");
78         return 0;
79     }
80     xfs();
81     return 0;
82 }

2、Jeff搞出来的90分。直接暴力枚举,两层循环交换第几个点。这里不多说,毕竟我没写~~

3、正解 100分。建立一个新的数组v[],v[i]=26^(i-1) mod M,将v[1..L]作为右数第1..L位的新权值,最后计算每一位的数字与它对应的新权值之积的和,即:
Sum = SUM{ num[i]*v[i] | 1 <= i <= N }
如果Sum能够整除M,则原26进制能够整除M。剩下的事就是枚举每一对字母进行交换,再进行判断。公式如下:
NewSum = Sum–num[i]*v[i]-num[j]*v[j]+num[i]*v[j]+num[j]*v[i]

 1 /* 
 2    直接附上std代码非手打
 3 */
 4 #include <iostream>
 5 #include <cstdio>
 6 using namespace std;
 7 #define MAXL 1024
 8 char str[MAXL];
 9 int fir[MAXL], rest[MAXL];
10 int L,MOD;
11 
12 void init()
13 {
14     freopen("patchouli.in","r",stdin);
15     freopen("patchouli.out","w",stdout);
16 }
17 
18 void readdata()
19 {
20     scanf("%s", str);
21     scanf("%d", &MOD);
22 }
23 
24 void work()
25 {
26     L=0;//对L初始化 
27     for (int i=0;str[i];i++)//将咒语转换为26进制数 
28         fir[++L]=str[i]-'A';//循环记录下每个字符代表的数 
29     long long tp,sum=0;
30     rest[L]=1,sum=fir[L];
31     for (int i=L-1;i>=1;i--)//取模的优化(降低难度)
32     {
33         rest[i]=(rest[i+1]*26)%MOD;//将26进制数转换为10进制 
34         sum+=rest[i]*fir[i]; 
35     }//计算每一位的数字与它对应的权值之积的和,最后取得能否被MOD整除 
36     if (sum%MOD==0) printf("0 0\n");
37     else
38     {//不能整除就一个一个试,直到解决问题。 
39         int x,y,ans_x,ans_y;
40         bool flag=false;
41         for (x=1;x<L&&!flag;x++)
42             for (y=x+1;y<=L&&!flag;y++)//将xy换位置得到新的sum 
43             {
44                 tp=sum-rest[x]*fir[x]-rest[y]*fir[y];
45                 tp+=rest[x]*fir[y]+rest[y]*fir[x];
46                 //公式自己推
47                 //newsum = sum-num[i]*fir[i]-num[j]*fir[j]+num[i]*fir[j]+num[j]*fir[i]
48                 if (tp%MOD==0)//判断新的sum能否满足条件 
49                 {
50                     ans_x=x, ans_y=y;
51                     flag=true;
52                 }
53             }
54         if (!flag) ans_x=ans_y=-1;//如果还是不能解决,那就-1 -1吧。 
55         printf("%d %d\n",ans_x,ans_y);
56     }
57 }
58 
59 int main()
60 {
61     init();
62     readdata();
63     work();
64     return 0;
65 }

 

 

 

5、比赛

(match.pas/c/cpp)

1s / 128M

【问题描述】

山山非常喜欢看足球赛,但因为沉迷于刷集训队作业,错过了最近的一次足球联赛。此次联赛共n支球队参加,比赛规则如下:

(1) 每两支球队之间踢一场比赛。

(2) 若平局,两支球队各得1分。

(3) 否则胜利的球队得3分,败者不得分。

尽管非常遗憾没有观赏到精彩的比赛,但山山通过新闻知道了每只球队的最后总得分,然后聪明的他想计算出有多少种可能的比赛过程。

譬如有3支球队,每支球队最后均积3分,那么有两种可能的情况:

 

但山山发现当球队较多时,计算工作量将非常大,所以这个任务就交给你了。请你计算出可能的比赛过程的数目,由于答案可能很大,你只需要输出答案对10^9+7取模的结果。

【输入】

输入文件match.in。

第一行是一个正整数 n,表示一共有 n 支球队。

接下来一行 n 个非负整数,依次表示各队的最后总得分。

【输出】

输出文件match.out。

包含一个整数,表示答案对 10 9 +7 取模的结果。

【输入输出样例】

match.in

match.out

4

4 3 6 4

3

 

【数据说明】

20%:n≤4;

40%:n≤6;

60%:n≤8;

100%:n≤10 且至少存在一组解。

Solution:

(懒得自己写了,正解解析)

HNOI2013比赛:记忆化搜索。如果我们直接进行暴力搜索的话,会出现若干重复的情况,那么这个时候我们需要进行判重性剪枝,最直接的方式就是记录已经走过的状态。状态固然是当前每个队伍的得分,因为n<=10,但是即便这么小也不能直接存储,这里我们加一个long long的哈希来存储,开map进行判重。为什么这么暴力的办法是可行的?我们对状态数量进行一下分析就可以大胆使用了。10个队,对于每个队,最多比9场,得27分,那么放入hash函数中,最大为7.77*10^12,在long long范围内,用map存一下就行了。

 

搜索的过程也是有讲究的。我们将每支队伍的得分从大到小排序,将得分多的队伍优先进行搜索,每次与当前得分最低的队伍进行匹配,这样可以很快搜完。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <map>
 4 using namespace std;
 5 
 6 #define MAXN 15
 7 #define MOD 1000000007
 8 
 9 typedef long long ll;
10 
11 struct Cmp
12 {
13   bool operator () (int a, int b)
14   {
15     return a > b;
16   }
17 };
18 Cmp x;
19 
20 ll a[MAXN];
21 
22 map <ll, ll> mp;
23 
24 ll hash(int o)
25 {
26   ll res = o, tmp[MAXN];
27   for (int i = 1; i <= o; i++) tmp[i] = a[i];
28   sort(tmp + 1, tmp + o + 1, x);
29   for (int i = 1; i <= o; i++) res += res * 28 + tmp[i];
30   return res;
31 }
32 
33 ll DFS(int o, int n)
34 {
35   if (a[n] > 3 * (n - o)) return -1;
36   ll res = 0;
37   if (o == n)
38   {
39     if (n == 1) return 1;
40     else
41     {
42       ll h = hash(n - 1);
43       if (mp[h]) return mp[h];
44       return mp[h] = DFS(1, n - 1);
45     }
46   }
47   if (a[n] >= 3) 
48   {
49     ll tmp = 0;
50     a[n] -= 3, tmp = DFS(o + 1, n);
51     if (tmp != -1) (res += tmp) %= MOD;
52     a[n] += 3;
53   }
54   if (a[n] && a[o])
55   {
56     ll tmp = 0;
57     a[n]--, a[o]--, tmp = DFS(o + 1, n);
58     if (tmp != -1) (res += tmp) %= MOD;
59     a[n]++, a[o]++;
60   }
61   if (a[o] >= 3)
62   {
63     ll tmp = 0;
64     a[o] -= 3, tmp = DFS(o + 1, n);
65     if (tmp != -1) (res += tmp) %= MOD;
66     a[o] += 3;
67   }
68   return res ? res : -1;
69 }
70 
71 int n;
72 
73 int main()
74 {
75   freopen("match.in", "r", stdin);
76   freopen("match.out", "w", stdout);
77   scanf("%d", &n);
78   for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
79   sort(a + 1, a + n + 1, x);
80   printf("%d", DFS(1, n));
81   return 0;
82 }

 

 

 

 

posted @ 2017-10-09 20:40  five20  阅读(469)  评论(0编辑  收藏  举报
Live2D