这还是没有考什么几何题物理题,要是考了,岂不完蛋?

代码奇丑无比。。

 

A: 哦吼?GJC要防AK了?

如果x是y的倍数输出-1,否则输出x。

#include<cstdio>
#include<cctype>
#include<vector>
#include<iostream>
using namespace std;
 
int x,y;
 
int main()
{
    scanf("%d%d",&x,&y);
    if(x%y==0)printf("-1\n");
    else printf("%d\n",x);
    return 0;
}

B: 730的天花板

斐波那契变形。对于3*n的矩阵,考虑最左边的一列,只有两种选择,要么放2*2的,要么不放。设f(k)为3*k矩阵方案数,如果最左边放2*2,则占了两行,剩下两个位只能放2个1*1的,方案数2*f(n-2),如果不放2*2的,就是只放1*1的,只能放3个1*1的,方案数f(n-1).故递推公式f(n)=f(n-1)+2*f(n-2).递推边界f(1)=f(0)=1。

#include<cstdio>
#include<cctype>
#include<vector>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
 
int t,n,a,b,c;
int f[10000005];
int main()
{
    scanf("%d",&t);
    f[0]=f[1]=1;
    for(int i=2;i<=10000000;i++)f[i]=(f[i-1]+2*f[i-2])%19260817;
    while(t--)
    {
        scanf("%d",&n);
        printf("%d\n",f[n]);
    }
    return 0;
}

C: gugugu

模拟

#include<cstdio>
#include<cctype>
using namespace std;
 
int t,n;
char s[1005];
 
bool ok(int n)
{
    if(n<=1)return 0;
    for(int i=2;i<=n-1;i++)if(n%i==0)return false;
    return true;
}
 
int main()
{
    scanf("%d",&t);
    int cnt=0;
    while(t--)
    {
        scanf("%s",s);
        for(int i=0;s[i];i++)if(tolower(s[i])=='g'&&tolower(s[i+1])=='u')cnt++;
    }
    printf(ok(cnt)?"gugugu\n":"gu\n");
    return 0;
}

D: 不可思议唤来不可思议β

排列组合题,显然枚举会超时。比赛时思路是对的,但是忘了含有重复元素的排列数怎么算,使劲推公式,没有思路,无果,,回来一查,这不是高中数学排列组合裸题吗!

假设有 111223这6个数,如何计算它的全排列呢?先假设这6个数字都不相同,排列共A(6,6)种,但在这A(6,6)种排列中,3个1和2个2是一样的,但被计算了A(3,3),A(2,2)种,因此答案就是A(6,6)/A(3,3)/A(2,2)种,这就是这道题最关键的地方。

先考虑最左边那一位可以填1,2,3三种,可以确定全排列中最高位分别为1,2,3时排列的个数,设排列总数为x,则最高位的数字可以确定加起来等于x*10^5,其实全排列先考虑哪一个位都是一样的,其他每一位都可以这样考虑,结果就是x*10^5+x*10^4+x*10^3+x*10^2+x*10+x.

这个x又怎么确定呢?当填1时排列数为1*(11223的全排列数),填2时排列数为2*(11123的全排列数),填3时排列数为3*(11122的全排列数),就是这样的思路。

下来写时候一处从0开始脑抽写出1开始,找不出错,然后花好多时间和next_permutation对拍才找出来错误。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
 
int n,num[10];
long long fact[10],ans;
 
int main()
{
    //freopen("input.in","r",stdin);
    fact[0]=fact[1]=1;
    for(int i=2;i<10;i++)fact[i]=fact[i-1]*i;
    while(scanf("%d",&n)&&n)
    {
        memset(num,0,sizeof(num));
        ans=0;
        for(int i=0,k;i<n;i++)
        {
            scanf("%d",&k);
            num[k]++;
        }
         
        long long cnt=0,pos;
        for(int i=0;i<10;i++)if(num[i])
        {
            pos=i*fact[n-1];
            for(int j=0;j<10;j++)
            {
                if(i==j)pos/=fact[num[j]-1];
                else pos/=fact[num[j]];
            }
            cnt+=pos;
        }
         
        for(int i=0;i<n;i++)ans=ans*10+cnt;
        printf("%lld\n",ans);
    }
    return 0;
}

E: Hacking to the Gate

导数用等价无穷小代换得到1.

F: 维生素

开始想的搜索剪枝,差不多写完才发现就是一个相当水的贪心。。只有5种搭配,记录7个价格就行了。记录a,b,c分别的最低价钱3个最低价钱,abc任意搭配2种在同一个药里的3个最低价钱,abc均含在同一个药里的最低价钱,共7给价钱。搭配的话单买abc1种,一种单一种双3种,abc一块买1种,共5种。注意(ab,bc)买这两个药归在了a+bc或者ab+c里,因为a保存的是含有a元素的药的最低价钱,其他价钱也是一样的道理,容易想象,这是正确的。

#include<cstdio>
#include<cctype>
#include<vector>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
 
int t,n;
vector<int> have[3];
int x;
char s[105];
int num[3];
int money[200];
 
int main()
{
    //freopen("input.in","r",stdin);
    scanf("%d",&t);
    while(t--)
    {
        for(int i=0;i<200;i++)money[i]=(1<<25);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<3;j++)num[j]=0;
            scanf("%d%s",&x,s);
            for(int j=0;s[j];j++)num[s[j]-'A']=1;
             
            if(num[0]==1)money[0]=min(money[0],x);
            if(num[1]==1)money[1]=min(money[1],x);
            if(num[2]==1)money[2]=min(money[2],x);
            if(num[0]==1&&num[1]==1)money[3]=min(money[3],x);
            if(num[1]==1&&num[2]==1)money[4]=min(money[4],x);
            if(num[0]==1&&num[2]==1)money[5]=min(money[5],x);
            if(num[0]==1&&num[1]==1&&num[2]==1)money[6]=min(money[6],x);
        }
        bool ok=1;
        for(int j=0;j<=2;j++)if(money[j]==(1<<25))ok=0;
        if(!ok)printf("-1\n");
        else
        {
            int ans=(1<<30);
            ans=min(ans,money[0]+money[1]+money[2]);
            ans=min(ans,money[3]+money[2]);
            ans=min(ans,money[4]+money[0]);
            ans=min(ans,money[5]+money[1]);
            ans=min(ans,money[6]);
            printf("%d\n",ans);
        }
 
    }
    return 0;
}

G: 你选择的这个世界

比赛时一直想着什么欧拉函数,很复杂,,结果那么多人过??回来经提醒,打表找规律,结果就是平方数嘛,太明显了,仔细一想,确实是,一开始全是反面向上,要想达到正面向上,必须翻奇数次,也就是说,第n枚硬币要想正面向上,必须满足n的因数个数为奇数,比如说16,因数有1,16,,2,8,,4.也就是说,除了sqrt(n)外,因数都是成双成对出现的,故只有平方数的sqrt为整数。

#include<iostream>
using namespace std;
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i*i<=n;i++)printf("%d ",i*i);
        putchar('\n');
    }
    return 0;
}

H: 邱总的游戏

这题感觉很难想,确实很难想,反正我这等渣渣是想不出来的。后来群里出题人讲的:必胜状态是谁先构造出Q_ _Q并且剩余空位有偶数个时谁就必胜。因为对方若填入2个Q之间的某个空位某个字符,自己方就一定胜,对方填入其他的空格,自己再填到其他的空格,自己总能赢。

先笔推前面几种n小的,发现1<=n<=6时,一定是平局。

n=7时ksf胜,因为当n=7时,ksf把Q放中间,然后左三右三空,qz无论怎么放,ksf总能构造必胜状态。当n为>=7的奇数时ksf必胜。

当为偶数时,ksf一定不能先放Q,因为这样的话qz接着隔两位放一个Q就必胜了,因此ksf先放A,A的隔壁两个位置qz是不能放Q的,否则马上输。于是,A往两边放,连续的不与A紧邻的空格数越多,只要个数>=7,qz就必胜,因此ksf的策略是往中间放(偶数的中间两个任意一个),于是qz必胜的情况为n/2-1>=7,即n>=16.当n为8~14之间的偶数时,必为平局。

#include<iostream>
#include<algorithm>
using namespace std;

int t,n; 
char *ans[3]={"Draw","ksfnb","qznb!"};

int main()
{	
	cin>>t;
	while(t--)
	{
		cin>>n;
		int ok;
		if(n<7)ok=0;
		else
		{
			if(n&1)ok=1;
			else if(n<16)ok=0;
			else ok=2;
		}
		cout<<ans<<endl;
	}
	return 0;
}

I: 二律的日常

比赛时没写出来。比赛时开始想着先排序,这个思路显然是错误的,题目要依次拿,假若礼物价格依次是12,3,4,7,需要2个礼物,若排序,拿3+4+(7-1)=13元,而实际上拿13的话会买12的,剩下1元就不够了。又想到二分,但实际上题目是不能用二分的,因为买的礼物个数并不随着拥有钱数增加而增加,比如礼物12,1,1元,我拿12元买1个,拿2元反而买2个。

正确的做法是贪心,代码实现有许多细节需要注意。如果n=k则ksf带多少钱都可以,如果礼物0元个数>k则不可能买只买k件。0元礼物很特殊,必须买,没有余地。其他正常情况的话,设有num个0元礼物,则还需从左往右买k-num个非0礼物,再取右边最低的非0元礼物价格-1加到结果上,就可以了。因为n>k,k>=num,(k-num)+num<n,因此右边一定存在这样的非0元礼物。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
 
int t,n,k,a[100005];
 
int main()
{ 
    //freopen("input.in","r",stdin);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
         
        if(n==k)printf("ksfnb\n");
        else
        {
            int num=0;
            for(int i=1;i<=n;i++)if(!a[i])num++;
            if(num>k)printf("alsnb\n");
            else
            {
                long long ans=0;
                int cnt=0;
                int i;
                for(i=1;cnt<k-num;i++)if(a[i])
                {
                    cnt++;
                    ans+=a[i];
                }
                i--;
                int minn=(1<<30);
                while(++i<=n)if(a[i])minn=min(minn,a[i]);
                ans+=minn-1; 
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
} 

 

J: 救救狗哥吧

这大水题比赛时硬是过不了。。回来理清思路很容易呀,看比赛时的代码就是写太乱了,逻辑也被乱代码搞晕了,于是就过不了,这提醒我,比赛时也不能一味求快,把代码写的很凌乱,如果水题实在过不了,不妨重新写一遍,说不定就过了,浪费不了多少时间的。还有,这题不用eps处理浮点误差也没问题,因为0.5可以被二进制精确表达,不过尽量带个eps吧。

#include<iostream>
#include<cmath>
using namespace std;
 
int t,a,b,c; 
 
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&a,&b,&c);
        double x=-1.0*b/a/2;
        int x1=floor(x),x2=ceil(x);
        if(fabs(fabs(x-x1)-fabs(x-x2))<0.0000001)x1=x1;
        else if(fabs(x-x1)>fabs(x-x2))x1=x2;
        printf(a>0?"qznb!\n":"ksfnb!\n");
        printf("%d %lld\n",x1,a*x1*x1+b*x1+(long long)c);
    }
    return 0;
}

 

K: 驯养人

生成斐波那契数列,记录一个斐波那契数是第几个斐波那契数,然后暴力搜索。

#include<cstdio>
#include<cctype>
#include<vector>
#include<iostream>
using namespace std;
 
int t,n;
int ok[200005],f[200005],to[200005];
vector<int> v;
 
bool dfs(int n)
{
    if(ok[n])
    {
        v.push_back(n);
        return 1;
    }
    else
    {
        for(int i=n-1;i>=1;i--)if(ok[i])
        {
            if(dfs(n-i))
            {
                v.push_back(i);
                return 1;
            }
        }
    }
    return 0;
}
 
int main()
{
    scanf("%d",&n);
    f[0]=f[1]=1;
    ok[1]=1;to[1]=1;
    for(int i=2;;i++)
    {
         
        f[i]=f[i-1]+f[i-2];
        if(f[i]>=200000)break;
        ok[f[i]]=1;to[f[i]]=i;
    }
     
    dfs(n);
    int s=v.size();
    printf("xufu1e\n%d",to[v[s-1]]);
    for(int i=s-2;i>=0;i--)printf(" %d",to[v[i]]);
    putchar('\n');
    return 0;
}