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;
}