第三届大中学生社团ACM程序设计大赛题解

版权声明:本文为博主瞎写的,请随便转载 https://blog.csdn.net/sdut_jk17_zhangming/article/details/82850936

A Alice 和 Bob

题意:两人取一堆石子 石子有n个  先手第一次不能全部取完但是至少取一个 之后每人取的个数不能超过另一个人上一次取的数的K倍 拿到最后一颗石子的赢 先手是否有必胜策略 若有 先手第一步最少取几个 
思路:
(1)首先k=1的时候 必败态是2^i 因为把数二进制分解后 拿掉二进制的最后一个1,那么对方必然不能拿走倒数第二位的1 因为他不能拿的比先手多 先手只要按照这个策略对方一直都不可能拿完 所以你就会赢
(2)k=2的时候,必败态是斐波那契数列。因为任何一个整数n都可以写成若干项不相邻的斐波那契数的和,所以我们拿掉1,对方永远取不完再高位的1。因为斐波那契数列两项f[i]和f[i+2]满足f[i+2]>2*f[i]。比如设斐波那契数列为1,2,3,5,8,13……12=8+3+1,化成二进制就好比是10101,那么你拿走最右边的1(其实就是1),那么对方不可能拿走第三位的1(这个1其实是3),这样就和 k=1一个道理,对方不可能拿完,所以你就能拿完;
(3)k>=3的时候,我们必须构造数列,将n写成数列中一些项的和,使得这些被取到的项的相邻两个倍数差距>k 那么每次去掉最后一个1 还是符合上面的条件。设这个数列已经被构造了i 项,第i项为a[i],前i项可以完美对1到b[i] 编码使得每个编码的任意两项倍数>K 那么有a[i+1]=b[i]+1;这是显然的。因为b[i]+1没法构造出来。只能新建一项表示。然后计算b[i+1]。 既然要使用 a[i+1] 那么下一项最多只能是某个a[t] 使得 a[t]*K

代码

#include <iostream>

 

using namespace std;

#define N 1000000

int a[N],b[N];

int main()

{

    int t;

    scanf("%d",&t);

    for(int cas=1;cas<=t;cas++)

    {

        int n,k;

        scanf("%d%d",&n,&k);

        a[0] = b[0] = 1;

        int i=0,j=0;

        while(a[i]<n)

        {

            i++;

            a[i] = b[i-1] + 1;

            while(a[j+1]*k<a[i]) j++;

            if(a[j]*k<a[i])  b[i] = b[j] + a[i];

            else b[i] = a[i];

        }

        printf("Case %d: ",cas);

        if(a[i]==n) puts("lose");

        else

        {

            int ans =0;

            while(n)

            {

                if(a[i]<=n)

                {

                    n -= a[i];

                    ans = a[i];

                }

                i--;

            }

            printf("%d\n",ans);

        }

    }

    return 0;

}

 

 

C 百木棒

题意

给你一些数字 由木棒构成 移动这些木棒 构成相同长度最大的数字

思路

先输出每个数字对应的木棒个数  0-6 1-2 2-5 3-5 4-4 5-5 6-6 7-3 8-7 9-6最少的是1-2 最多的是8-7 最大的数是9-6

统计给出的木棒树num

如果num > = 6*n 那么最优只摆8 9

如果num == 2*n 只能摆1

如果num == 2*n+1  摆一个7 剩下摆1

如果num == 2*n+2 如果n>=2  摆两个7 剩下摆1 不然摆一个5

如果num == 2*n + 3 如果n >= 3 摆三个7  n==2 摆74 n==1 摆5

不然 摆9 num-=6 n-- 继续判断

代码

#include <iostream>

 

using namespace std;

char  a[100005];

int main()

{

    int n,i,j,num;

    int t;

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d%s",&n,a);

        num = 0;

        for(i=0;i<n;i++)

        {

            if(a[i]=='0'||a[i]=='6'||a[i]=='9') num+=6;

            if(a[i]=='1') num+=2;

            if(a[i]=='2'||a[i]=='3'||a[i]=='5') num+=5;

            if(a[i]=='4') num+=4;

            if(a[i]=='7') num+=3;

            if(a[i]=='8') num+=7;

        }

        int x,y;

        while(n)

        {

            if(num > 6*n){

                x = num - 6*n;

                for(i=1;i<=n-x;i++) printf("9");

                for(i=1;i<=x;i++) printf("8");

                break;

            }

            if(num == 2*n){

                for(i=1;i<=n;i++) printf("1");

                break;

            }

            if(num == 2*n+1){

                printf("7");

                for(i=1;i<=n-1;i++) printf("1");

                break;

            }

            if(num == 2*n+2)

            {

                if(n==1) {printf("4");break;}

                printf("77");

                for(i=1;i<=n-2;i++) printf("1");

                break;

            }

            if(num == 2*n+3){

                if(n==2) {printf("74");break;}

                if(n==1) {printf("5");break;}

                printf("777");

                for(i=1;i<=n-3;i++) printf("1");

            }

            printf("9");

            n--;

            num-=6;

        }

        printf("\n");

    }

    return 0;

}

 

 

 

 

 

E 显示屏

题意

给你一个放大倍数s 和 一串数字 把他们放大

思路

每个数字可以以分解为五部分

第一行 前后空格 中间空格或‘-’

第二至s行 前后‘|’或空格 中间空格

第s+2行 前后空格 中间空格或‘-’

第s+3 至 2*s+2 行  前后‘|’或空格 中间空格

第s+3行 前后空格 中间空格或‘-’

 

 

(比赛时把 ‘-’ 看成了 ‘_’  wa好多次)

代码

#include <iostream>

#include <cstring>

using namespace std;

char n1[11] = {"- -- -----"}; //每个数字的该位置的符号

char n2[11] = {"|   ||| ||"};

char n3[11] = {"|||||  |||"};

char n4[11] = {"  ----- --"};

char n5[11] = {"| |   | | "};

char n6[11] = {"|| |||||||"};

char n7[11] = {"- -- -- --"};

char a[20];

int main() {

int s;

int m,n,i,j,k;

while(true) {

scanf("%d%s",&s,a);

n = strlen(a);

if(s == 0) break;

for(i=0; i<n; i++) { // 结果的第一行

m = a[i] - '0'; //该位数字

printf(" "); //前后空格

for(j=0; j<s; j++) printf("%c",n1[m]);//s倍的符号

printf("  ");// 前后空格

}

printf("\n");

for(i=0; i<s; i++) {

for(j=0; j<n; j++) {

m = a[j]-'0';

printf("%c",n2[m]); //该位符号

for(k=0; k<s; k++) printf(" "); //中间空格

printf("%c ",n3[m]);//该位符号

}

printf("\n");

}

for(i=0; i<n; i++) {

m = a[i] - '0';

printf(" ");

for(j=0; j<s; j++) printf("%c",n4[m]);

printf("  ");

}

printf("\n");

for(i=0; i<s; i++) {

for(j=0; j<n; j++) {

m = a[j]-'0';

printf("%c",n5[m]);

for(k=0; k<s; k++) printf(" ");

printf("%c ",n6[m]);

 

}

printf("\n");

}

for(i=0; i<n; i++) {

m = a[i] - '0';

printf(" ");

for(j=0; j<s; j++) printf("%c",n7[m]);

printf("  ");

}

printf("\n");

}

return 0;

}

 

 

G 数石子

题意:求C(n,0)+C(n,1)+……+C(n,m)的值。

思路:由于t和n数值范围太大,所以此题查询复杂度不能太高,由组合数的将前k项求和可以推出,从而可以转换成莫队的区间查询,将n当成l,m当成r即可。此题需要注意,对于求组合数得用o(1)的方法求,也就是阶乘相除的方法,对于分母我们得求逆元,因而借助欧拉定理。

代码

#include<iostream>

#include<stdio.h>

#include<cmath>

#include<algorithm>

using namespace std;

const int mod=1e9+7;

#define ll long long

const int maxn=1e5+7;

ll jiecheng[maxn],inv[maxn];

ll ans[maxn];

int block;

ll qsm(ll a,ll b)

{

    ll ans=1;

    while(b){

        if(b&1)

            ans=ans*a%mod;

        a=a*a%mod;

        b>>=1;

    }

    return ans;

}

void init()

{

    jiecheng[1] = 1;

    for(int i = 2; i < maxn; i++)

        jiecheng[i] = jiecheng[i-1] * i % mod;

    for(int i = 1; i < maxn; i++)

        inv[i] = qsm(jiecheng[i], mod-2);

}

struct node{

    int l,r;

    int i;

}modui[maxn];

bool cmp(node a,node b)

{

    if(a.l/block==b.l/block)

        return a.r<b.r;

    return a.l<b.l;

}

ll C(ll n,ll m)

{

    if(m == 0 || m == n) return 1;

    ll ans=1;

    ans=(jiecheng[n]*inv[m])%mod*inv[n-m];

    ans=ans%mod;

    return ans;

}

int main()

{

    init();

    block = sqrt(maxn);

    int t;

    scanf("%d",&t);

    for(int i=0;i<t;i++)

    {

        scanf("%d%d",&modui[i].l,&modui[i].r);

        modui[i].i=i;

    }

    sort(modui,modui+t,cmp);

    int l=1,r=0;

    int sum=1;

    for(int i = 0; i < t; i++)

    {

        while(l < modui[i].l) sum = (2 * sum - C(l++, r) + mod) % mod;

        while(l > modui[i].l) sum = ((sum + C(--l, r))*inv[2]) % mod;

        while(r < modui[i].r) sum = (sum + C(l, ++r)) % mod;

        while(r > modui[i].r) sum = (sum - C(l, r--) + mod) % mod;

        ans[modui[i].i] = sum;

    }

    for(int i=0;i<t;i++)

    {

        printf("%lld\n",ans[i]);

    }

    

    return 0;

}

 

猜你喜欢

转载自blog.csdn.net/sdut_jk17_zhangming/article/details/82850936