zjgsu新生赛题解

大一新生好强啊,oi大佬一个半小时就AC 7题了,orz...

几个学长学姐无聊看代码的时候竟然发现了一个神仙老哥!

zjgsu新生赛题解_第1张图片

zjgsu新生赛题解_第2张图片

膜,ORZ...

0 钞哥拒绝吃土:

这是一道水题,遍历这个数组把每个宝贝的价值保存下来相加即可。唯一一个坑点是最后的和会爆int,用long long即可。(PS:本来这题想搞点事情的,比如每个价值可能是小数,最后的答案要用大数等等,后来想想签到题还是不搞你们了~善良的学长学姐)

#include 
using  namespace std;

char str[1111];

int main()
{
	while (cin >> str)
	{
		int ans[105];
		for (int i = 0;i < 104;i++)
			ans[i] = 0;
		int bg = 0, ed = 0;
		int len = strlen(str);
		for (int i = 0;i <= len;i++)
		{
			if (str[i] == '+'||str[i] == 0)
			{
				int fla = 0;
				int tmp = 0;
				for (int j = ed - 1;j >= bg;j--)
				{
					ans[tmp] += str[j] - '0';
					if (ans[tmp] > 9)
					{
						ans[tmp] -= 10;
						ans[tmp + 1] += 1;
					}
					tmp++;
				}
				ed = bg = i + 1;
			}
			else if ('0' <= str[i] && str[i] <= '9')
			{
				ed++;
			}
		}
		int k = 101;
		while (!ans[k])k--;
		for (int i = k;k >= 0;k--)
			cout << ans[k];
		cout << endl;
	}
}

1 钞哥的钞票:

这也是一道水题,暴力遍历数组判断即可。(全场最水~)

#include 
using namespace std;
int main()
{
	char s[1000][1000];
	int n, i, k, m;
	while (scanf("%d", &n),n)
	{
		for (i = 0; i < n; i++)
		{
			gets(s[i]);
			m = strlen(s[i]);
			for (k = 0; k + 3 < m; k++)
			{
				if ((s[i][k] == 'C' || s[i][k] == 'c') && (s[i][k + 1] == 'H' || s[i][k + 1] == 'h') && (s[i][k + 2] == 'A' || s[i][k + 2] == 'a') && (s[i][k + 3] == 'O' || s[i][k + 3] == 'o'))
				{
					printf("%s\n", s[i]);
					break;
				}
			}
		}
	}
    return 0;
}

3 钞哥的百变二进制:

这题也是水题(会不会被打?逃)。根据题意,当b[i]为1时,无论a[i]为0还是为1,按位或之后第i位的值都为1,所以这种情况就不需要考虑了。考虑b[i]为0的情况,第一感觉就是若a[i]为0,则需找a[i]为1的有几位即可;若a[i]为1,则需找a[i]为0的有几位即可。但这样会存在重复的情况,就比如说样例。所以标解是统计b[i]为1的那些位中a[i]=0的个数num0,a[i]=1的个数num1,b[i]为0的那些位中a[i]=1的个数tmp0,a[i]=1的个数tmp1。ans = tmp0*(num1+tmp1)+tmp1*num0 。

#include
using namespace std;
 
typedef long long ll;
 
const int MAX = 1e5+10;
const ll mod = 1e9+7;
 
int n;
char a[MAX],b[MAX];
 
int main()
{
    scanf("%d",&n);
    int num0=0,num1=0;
    int tmp0=0,tmp1=0;
    scanf("%s%s",a,b);
    for(int i=0;i

4 钞哥抢金块:

一个非常简单的博弈,当(n+1)为3的倍数时先手必败,否则先手必胜。

#include
using namespace std;

typedef long long ll;

const int MAX = 1e5+10;
const ll mod = 1e9+7;

int n;

int main()
{
    while(scanf("%d",&n)!=EOF){
        if((n+1)%3==0){
            printf("PangPang will get the gold nugget!\n");
        }
        else{
            printf("Brother Chao will get the gold nugget!\n");
        }
    }
    return 0;
}

5 钞哥之遗忘曲线:

这题原来是个水题,c的范围为[1,1000],后来被一个学长改成了[1,1e15],太过分了!标解是先离散化,再用一个数组映射即可。离散化:虽然c<=1e15,但n只有5e6个,所以最多出现n个不同的数字,把这些不同的数字重新标号,就能用一个数组存下,然后去映射啦!具体实现可以用先排序然后依次标号或直接用stl的map。

#include
using namespace std;

typedef long long ll;

const int maxn = 1e6 + 10;
map mp;
int nums[maxn];
char strs[2][5] = {"Yes","No"};

int main()
{
    int n,q;
    ll num;
    scanf("%d%d",&n,&q);
    for(int i = 0;i < n;i++)
    {
        scanf("%lld",&num);
        mp[num] = i;
    }
    for(int i = 0;i <= n;i++)
        nums[i] = 0;
    char c[2];
    for(int i = 0;i < q;i++)
    {
        scanf("%s%lld",c,&num);
        int t = mp[num];
        if(c[0] == 'C')
        {
            nums[t] ^= 1;
        }
        else
        {
            printf("%s\n",strs[nums[t]]);
        }
    }
    return 0;
}

6 解救男朋友2:

简单dp。开一个dp[2][1024],前一维表示2个状态间(now、pre)的转移,后一个表示当前状态下和为sum的情况数。动态转移方程为:dp[now][j+a[i]]+=dp[pre][j] 。

#include 
using namespace std;
 
typedef long long ll;
 
ll a[120];
ll dp[2][1024];
int n,m;
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        int pre=0,now=1;
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=m;j++){
                dp[now][j]=dp[pre][j];
            }
            for(int j=0;j<=m;j++){
                if(dp[pre][j]&&j+a[i]<=m){
                    dp[now][j+a[i]]+=dp[pre][j];
                }
            }
            pre=!pre;
            now=!now;
        }
        printf("%lld\n",dp[pre][m]);
    }
    return 0;
}

7 钞哥与数字2:

这题是一个超级简单粗暴的打表题。可以看到数据组数T非常大,所以不能对每组数据都算一遍上述公式得到答案。因此,我们可以在T这个循环外打表,即把所有n的情况都算出来,保存在数组中。最后对于T组数据每个询问直接输出即可。

#include 
 
using namespace std;
 
typedef long long ll;
 
const int N = 100005;
 
inline ll f(ll x) {
    return x & 1 ? 3 * x + 1 : x / 2;
}
 
ll p[N] = {0};
 
int main() {
    for (int i = 100000; i; i--) {
        ll x = i;
        while (x >= 10) {
            x = f(x);
        }
        p[i] = x;
    }
    int t;
    scanf("%d", &t);
    while (t--) {
        int x;
        scanf("%d", &x);
        printf("%lld\n", p[x]);
    }
    return 0;
}

8 钞哥之家产继承:

这题是一个防ak题,一个很巧妙的思维题。对等式进行拆分:sum()为求和,csdn公式竟然爆炸了!

sum(g=0 -> nm) (g+1)Ag = sum(g=0 -> nm) gAg + sum(g=0 -> nm) Ag

后半部分等于A0+A1+…+Anm,就等于所有可能的情况数 = k^(nm)。

前半部分相当于每个格点的贡献和。贡献定义为每个格点当一次Great cell贡献就+1。
那么对于每个格点来说,当它选的数字是i,那么它所在行列的其他数字的选择种类数 = (i-1)^(n+m-2)种,剩余格点可以随便放,种类数 = k^(n-1)(m-1) 种,所以每个格点的贡献 = (i-1)^(n+m-2)* k^(n-1)(m-1),i属于[2,K]。
总共有nm个格点,所以最后的答案 =nm * sum(i=2 -> k) (i-1)^(n+m-2)* k^(n-1)(m-1) + k^(n*m) 。

#include
using namespace std;

typedef long long ll;

const int MAX = 1e5+10;
const ll mod = 1e9+7;

ll n,m,k;

//快速幂
ll multiply(ll a, ll b)
{
	ll ans = 1;
	while (b)
	{
		if (b & 1)
		{
			ans = ((ans%mod)*(a%mod)) % mod;
			b--;
		}
		b /= 2;
		a = ((a%mod)*(a%mod)) % mod;
	}
	return ans;
}

int main()
{
    int T;
    scanf("%d",&T);
    int Case=1;
    while(T--)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        ll ans=0;
        for(int i=2;i<=k;i++){
            ans+=multiply(i-1,n+m-2);
            ans%=mod;
        }
        ans*=(n*m);
        ans%=mod;
        ans*=multiply(k,(n-1)*(m-1));
        ans%=mod;
        ans+=multiply(k,n*m);
        ans%=mod;
        printf("Case #%d: %lld\n",Case++,ans);
    }
    return 0;
}

 

你可能感兴趣的:(zjgsu新生赛题解)