2021.10.20
T1:取石子游戏
Problem:
小林和亮亮正在玩一个取石子的游戏。
石子一共有 n 堆,其中第 i 堆恰好有 i 粒石子。小林先取,亮亮后取,并且两人依次轮流取石。每一次取石子的人可以选择任意一堆还未被取完的石子,并取走这一堆中任意多粒石子(注意,不能一粒石子也不取,也不能同时在多堆石子中取石)。最终,无石可取的人为败。
小林和亮亮都十分聪明,他们的每次取石都会采取最优策略。在经过多次游戏后,小林发现了先手必胜的条件,但他不满足于此,他想知道,在知道石子的堆数 n 后,他第一次取石有多少种方式可以获胜。
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct nim{ 4 int len,value[1010]; 5 nim(){len=1;memset(value,0,sizeof(value));} 6 nim operator =(const char* s){ 7 len=strlen(s); 8 memset(value,0,sizeof(value)); 9 for(int i=0;i<len;i++){ 10 value[i]=s[len-i-1]-48; 11 } 12 return *this; 13 } 14 nim operator =(const int& x){ 15 char s[1010]; 16 sprintf(s,"%d",x); 17 *this=s; 18 return *this; 19 } 20 nim operator +(const nim& x) const{ 21 nim y; 22 y.len=max(len,x.len); 23 for(int i=0;i<len;i++) y.value[i]=x.value[i]+value[i]; 24 int k=0; 25 for(int i=0;i<y.len;i++){ 26 k+=y.value[i]; 27 y.value[i]=k%10; 28 k/=10; 29 } 30 if(k>0){ 31 y.value[y.len]=k; 32 y.len++; 33 } 34 return y; 35 } 36 nim operator += (const nim& x){ 37 *this=*this+x; 38 return *this; 39 } 40 nim operator /= (const int& x){ 41 int p=0; 42 for(int i=len-1;i>=0;i--){ 43 p=p*10+value[i]; 44 value[i]=p/x; 45 p%=x; 46 } 47 if(!value[len-1]) len--; 48 return *this; 49 } 50 nim operator *= (const int& x){ 51 int p=0; 52 for(int i=0;i<len;i++){ 53 p+=value[i]*x; 54 value[i]=p%10; 55 p/=10; 56 } 57 if(p>0){value[len]=p;len++;} 58 return *this; 59 } 60 bool operator <=(const nim& x) const{ 61 if(len!=x.len) return len<x.len; 62 for(int i=len-1;i>=0;i--){ 63 if(value[i]<x.value[i]) return true; 64 if(value[i]>x.value[i]) return false; 65 } 66 return true; 67 } 68 nim operator -=(const nim& x){ 69 for(int i=0;i<len;i++) value[i]-=x.value[i]; 70 for(int i=0;i<len;i++) if(value[i]<0){value[i]+=10;value[i+1]--;} 71 while(len>1&&value[len-1]==0) len--; 72 return *this; 73 } 74 }a,b; 75 void writeint(nim x){ 76 for(int i=x.len-1;i>=0;i--) printf("%d",x.value[i]); 77 printf("\n"); 78 } 79 int main(){ 80 int T; 81 scanf("%d",&T); 82 while(T--){ 83 char s[1010]; 84 scanf("%s",s); 85 int n=strlen(s); 86 if((s[n-1]-48)%2){ 87 a=s,b=1,a+=b; 88 int k=s[n-1]-48+1; 89 if(n>1) k+=(s[n-2]-48)*10; 90 if(!(k%4)){printf("0\n");continue;} 91 a/=2; 92 writeint(a); 93 continue; 94 } 95 else{ 96 b=1; 97 a=s; 98 while(b<=a) b*=2; 99 b/=2; 100 a-=b; 101 b=1; 102 a+=b; 103 writeint(a); 104 } 105 } 106 return 0; 107 }
T2:魔数
Problem:
小林和亮亮是好朋友。小林有一个幸运数字 a,亮亮有一个幸运数字 b。一个数字称之为“幸运数”当且仅当它只由 a 和 b 组成。小林称一个数为“魔数”,当且仅当这个数各数位之和为“幸运数”,且这个数字本身也是“幸运数”。
举个例子:小林的幸运数字为 2,亮亮的幸运数字为 6,那么 23 不是“幸运数”,而 26、222 是“幸运数”。进一步,222 是“魔数”(因为 2+2+2=6),而 26不是“魔数”(因为 2+6=8)。
亮亮想要知道,有多少个 n 位的“魔数”(一个 n 位数不包含前导 0),由于这个数字会很大,所以亮亮只关心这个数模 1000000007(10^9+7,是一个质数)的结果。
Code:
1 #include<bits/stdc++.h> 2 #define modd 1000000007 3 using namespace std; 4 long long q_pow(long long x,long long v){ 5 long long ans=1; 6 while(v){ 7 if(v&1){ 8 ans*=x; 9 ans%=modd; 10 } 11 x*=x; 12 x%=modd; 13 v>>=1; 14 } 15 return ans; 16 } 17 long long fac[1000010],inv[1000010]; 18 int a,b,n; 19 void init(){ 20 fac[0]=1; 21 for(int i=1;i<=1000000;i++){ 22 fac[i]=fac[i-1]*i%modd; 23 } 24 inv[1000000]=q_pow(fac[1000000],modd-2); 25 for(int i=999999;i>=0;i--){ 26 inv[i]=inv[i+1]*(i+1)%modd; 27 } 28 } 29 bool is(long long x){ 30 while(x){ 31 int now=x%10; 32 if(now!=a&&now!=b) return 0; 33 x/=10; 34 } 35 return 1; 36 } 37 int main(){ 38 init(); 39 long long ans=0; 40 cin>>a>>b>>n; 41 for(int i=0;i<=n;i++){ 42 long long now=a*i+b*(n-i); 43 if(is(now)){ 44 ans+=(fac[n]*inv[i])%modd*inv[n-i]%modd; 45 ans%=modd; 46 } 47 } 48 cout<<ans<<endl; 49 }
T3:军训站队
Problem:
小林和亮亮刚入高中,首先就迎来了军训。这一天,他们班的同学正在教官的指导下进行队列练习。
班上总共有 n 位同学,他们站成一排,然而,本该站成一条直线的他们却排成了“一字长蛇阵”。用数学的语言来描述,如果把训练场看成一个平面直角坐标系,第 i 名同学所在位置的横坐标是 i,而所有同学的纵坐标本该是 0(或者任意一个相等的常量),这样就排成了一条直线。当然,由于同学们排的歪歪扭扭,所以第 i 位同学的横坐标依然是 i,而纵坐标却成了Yi (为了简化问题,我们假设所有的坐标都是整数)。
对此,教官当然十分生气,因此他决定下命令调整队伍,使得所有人能够站成一条直线(也即让所有的Yi 相同)。教官的命令总共有三种:
1、除了某一个同学外,其余所有同学向前走一步(向前走一步可理解为Yi 的值加 1,下同);
2、除了某一个同学外,其余所有同学向前走两步;
3、除了某一个同学外,其余所有同学向前走五步。
教官希望他能以最少的命令次数使得所有同学站成一条直线,但是他不会算,于是就让亮亮帮他来计算。亮亮虽然聪明,可是由于班上同学人数众多,他一下子也解决不了这个问题,只能来寻求会编程的你的帮助,你能告诉亮亮答案吗?
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,a[100001],b[100001]; 4 long long ans=0,ss; 5 int main(){ 6 scanf("%d",&n); 7 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 8 sort(a+1,a+n+1); 9 for(int i=n;i>=1;i--) a[i]-=a[1]; 10 memcpy(b,a,sizeof(b)); 11 for(int j=0;j<5;j++){ 12 ans=0; 13 memcpy(a,b,sizeof(b)); 14 for(int i=1;i<=n;i++) a[i]+=j; 15 for(int i=1;i<=n;i++){ 16 if(a[i]>=5) ans+=(long long)a[i]/5,a[i]%=5; 17 if(a[i]>=2) ans+=(long long)a[i]/2,a[i]%=2; 18 if(a[i]>0) ans+=(long long)a[i],a[i]=0; 19 } 20 if(j==0) ss=ans; 21 else ss=min(ss,ans); 22 } 23 printf("%lld\n",ss); 24 return 0; 25 }