2017年第0届浙江工业大学之江学院程序设计竞赛决赛题解(持续更新中)

这篇博客详细解析了2017年浙江工业大学之江学院程序设计竞赛的决赛题目,包括多个问题的描述、输入输出格式和样例。涉及的算法包括但不限于数论、矩阵、组合计数和序列求和,适合对算法和程序设计感兴趣的读者。
摘要由CSDN通过智能技术生成


Problem A: qwb与支教

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 1520   Solved: 295
[ Submit][ Status][ Web Board]

Description

qwb同时也是是之江学院的志愿者,暑期要前往周边地区支教,为了提高小学生的数学水平。她把小学生排成一排,从左至右从1开始依次往上报数。

玩完一轮后,他发现这个游戏太简单了。于是他选了3个不同的数x,y,z;从1依次往上开始报数,遇到x的倍数、y的倍数或z的倍数就跳过。如果x=2,y=3,z=5;第一名小学生报1,第2名得跳过2、3、4、5、6,报7;第3名得跳过8、9、10,报11。

那么问题来了,请你来计算,第N名学生报的数字是多少?

Input

多组测试数据,处理到文件结束。(测试数据数量<=8000)

每个测试例一行,每行有四个整数x,y,z,N。( 2≤x,y,z≤107,1≤N≤1017)。

Output

对于每个测试例,输出第N名学生所报的数字,每个报数占一行。

Sample Input

2 3 5 2
6 2 4 10000

Sample Output

7
19999

HINT


思路:算是水题把, 只要你会一点点点容斥的东西, 二分+容斥,我也只会 简单容斥...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll x, y, z, n;
ll lcm(ll a, ll b)
{
    return a/__gcd(a, b)*b;
}
ll get_cnt(ll a)  //这里据说会爆ll, 要用__int128
{

    return a - (a/x + a/y + a/z - a/lcm(x, y) - a/lcm(x, z) - a/lcm(y, z) + a/lcm(lcm(x, y), z));
}
int main()
{
    while(~scanf("%lld%lld%lld%lld", &x, &y, &z, &n))
    {
        ll l = 1, r = 9e17 + 5, mid, ans;
        while(l <= r)
        {
            mid = (l+r)/2;
            if(get_cnt(mid) >= n)
                ans = mid, r = mid - 1;
            else l = mid + 1 ;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

Problem B: qwb与矩阵

Time Limit: 2 Sec   Memory Limit: 128 MB
Submit: 1124   Solved: 227
[ Submit][ Status][ Web Board]

Description

做完了辣么多的数学题,qwb好好睡了一觉。但是他做了一个梦:

有一个n*m的矩阵,qwb在这个矩阵的左上角(1,1),终点在右下角(n,m)。

每个格子中有小钱钱,也可能没有,还有可能是要交过路费的, 并且行走方向必须是靠近终点的方向。
往下走一次只能走一格,往右走一次可以走一格也可以走到当前列数的倍数格。 
比如当前格子是(x,y),那么可以移动到(x+1,y),(x,y+1)或者(x,y*k),其中k>1。

qwb希望找到一种走法,使得到达右下角时他能够有最多的小钱钱。

你能帮助他吗?

Input

第一行是测试例数量 T (T<=100),接下来是T组测试数据。
每组测试数据的第一行是两个整数n,m,分别表示行数和列数(1<=n<=20,1<=m<=10000); 
接下去给你一个n*m的矩阵,每个格子里有一个数字 k (-100<=k<=100)代表小钱钱的数量。  ∑nm<=3,000,000 

Output

每组数据一行,输出L先生能够获得小钱钱的最大值(可能为负数)。

Sample Input

1
3 8
9 10 10 10 10 -10 10 10
10 -11 -1 0 2 11 10 -20
-11 -11 10 11 2 10 -10 -10

Sample Output

52
简单的dp。。 暴力转移不会t, 就暴力转移就好了。。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e4 + 5;
const int INF = 3e8;
int maze[25][maxn], n, m, dp[25][maxn];
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &maze[i][j]);
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= m; j++)
                dp[i][j] = -INF;
        dp[0][1] = dp[1][0] = 0;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
            {
                dp[i][j] = max(dp[i-1][j] + maze[i][j], dp[i][j]);
                dp[i][j] = max(dp[i][j-1] + maze[i][j], dp[i][j]);
                for(int k = 2; k*j <= m; k++) dp[i][k*j] = max(dp[i][j] + maze[i][k*j], dp[i][k*j]);
            }
        printf("%d\n", dp[n][m]);
    }
    return 0;
}

Problem C: 勤劳的ACgirls

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 151   Solved: 62
[ Submit][ Status][ Web Board]

Description

zjc的ACgirls队的队员最近比较忙,为了能够取得更好的比赛成绩,他们制定了一个m天a掉n题的计划,a掉一题可以是这m天的任何时候。
为了表示对acmer事业的热爱,队长wc要求每天必须至少要ac掉k题,这m天每天ac掉的题数可以用一个m元组表示。
设不同的m元组一共有c个,请问c的末尾有多少个0?(如果c是0,输出0)

Input

多组测试数据,处理到文件结束。(测试例数量<=160000)

输入的每一行是一个测试例,分别是m、n和k(0<=m,n,k<=1e9),含义如前所述。

Output

每组测试例中 m元组的数量的末尾 0的个数,占一行。

Sample Input

3 11 0
3 11 1
999 99999 4

Sample Output

0
0
5

思路:这个题也算比较经典,首先先计算组合数,怎样计算呢?首先隔板法,用隔板让每一块都有k个,然后就是n-m*k小球要插入到n个盒子,可以不放,这就是重复组合的知识了C(n-1,m+n-1);然后就是计算后面有几个0了,你会发现组合数的公式都是阶乘的,这也是经典问题了,看2跟5的数量谁的少。。除法就是上面的数量减去下面的就好了。。相关知识:重复组合

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll cal(ll x, ll y)  //计算x阶乘里有几个y的方法
{
    ll ans = 0;
    while(x)
    {
        ans += x/y;
        x /= y;
    }
    return ans;
}
int main()
{
    ll m, n, k;
    while(~scanf("%lld%lld%lld", &m, &n, &k))
    {
        if(m*k > n)
        {
            puts("0");
            continue;
        }
        ll fz = m-1, fm = n-m*k+m-1;
        ll five = cal(fm, 5) - cal(fm-fz, 5) - cal(fz, 5);  //组合数除法,相减就好了
        ll two = cal(fm, 2) - cal(fm-fz, 2) - cal(fz, 2);
        printf("%lld\n", min(two, five));
    }
    return 0;
}

Problem D: qwb与神奇的序列

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 1295   Solved: 222
[ Submit][ Status][ Web Board]

Description

qwb又遇到了一道题目:

有一个序列,初始时只有两个数x和y,之后每次操作时,在原序列的任意两个相邻数之间插入这两个数的和,得到新序列。举例说明:

初始:1 2
操作1次:1 3 2
操作2次:1 4 3 5 2
……
请问在操作n次之后,得到的序列的所有数之和是多少?

Input

多组测试数据,处理到文件结束(测试例数量<=50000)。

输入为一行三个整数x,y,n,相邻两个数之间用单个空格隔开。(0 <= x <= 1e10, 0 <= y <= 1e10, 1 < n <= 1e10)。

Output

对于每个测试例,输出一个整数,占一行,即最终序列中所有数之和。
如果和超过1e8,则输出低8位。(前导0不输出,直接理解成%1e8)

Sample Input

1 2 2

Sample Output

15

HINT

写几次操作,就会找到一个规律,假设第n次操作后的和为fn, 那么fn+1 = 3*fn - 1;

得到这个递推式就好做了,可以构造矩阵快速幂,也可以求数列的通项公式。

高中的待定系数法:


快速幂写法要注意: 用数列知识 容易算出答案是(3^n+1)/2, 本题要求mod1e8,2和1e8不互质,没有逆元, 易证 A%B/C=A%(B*C)/C, 那在快速幂的时候 %2e8就行了 很好用的技巧:(a/c)%b==((a%(b*c))/c)%b
快速幂代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Mod = 1e8;
ll quick_mul(ll p, ll q)
{
    ll ans = 1;
    while(q)
    {
        if(q&1) ans = (ans*p)%(2*Mod);
        p = (p*p)%(2*Mod);
        q >>= 1;
    }
    return ans;
}
int main()
{
    ll x, y, n;
    while(~scanf("%lld%lld%lld", &x, &y, &n))
    {
        printf("%lld\n", (x+y)%(2*Mod)*(quick_mul(3, n)+1)%(2*Mod)/2%Mod);
    }
    return 0;
}



Problem F: qwb has a lot of Coins

Time Limit:  1 Sec   Memory Limit:  128 MB
Submit:  756   Solved:  248
[ Submit ][ Status ][ Web Board ]

Description

qwb has a lot of coins. One day, he decides to play a game with his friend using these coins. He first puts some of his coins into M piles, each of which is composed of Ni (1<=i<=M) coins. Then, the two players play the coin game in turns. Every step, one can remove one or more coins from only one pile. The winner is the one who removes the last coin.
Then comes the question: How many different ways the first player can do that will ensure him win the game? 

Input

Input contains multiple test cases till the end of file. Each test case starts with a number M (1 <= M<= 1000) meaning the number of piles. The next line contains M integers Ni (1 <= Ni <= 1e9, 1 <= i<= M) indicating the number of coins in pile i.

Output

For each case, put the method count in one line.
If the first player can win the game, the method count is the number of different ways that he can do to ensure him win the game, otherwise zero.

Sample Input

3
1 2 3
1
1

Sample Output

0
1

kuangbin博弈题集里的原题..hdu1850

#include <iostream>  
#include <cstring>  
#include <cstdio>  
using namespace std;  
const int maxn = 105;  
int a[maxn];  
int main()  
{  
    int n;  
    while(~scanf("%d", &n) && n)  
    {  
        int sum = 0, ans = 0;  //sum初始值为0,因为所有的数字^sum等于它本身  
        for(int i = 1; i <= n; i++)  
            scanf("%d" ,&a[i]), sum ^= a[i];  
        for(int i = 1; i <= n; i++)  
            if(a[i] > (sum^a[i])) ans++;  //注意 ^ 优先级 比 > 小  
        printf("%d\n", ans);  
    }  
    return 0;  
}  

Problem G: qwb去面试

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 1789   Solved: 300
[ Submit][ Status][ Web Board]

Description

某一天,qwb去WCfun面试,面试官问了他一个问题:把一个正整数n拆分成若干个正整数的和,请求出这些数乘积的最大值。
qwb比较猥琐,借故上厕所偷偷上网求助,聪明的你能帮助他吗?

Input

第一行为一个正整数T.(T<=100000)
接下来T行,每行一个正整数n(n<=1e9),意义如题目所述。

Output

每一行输出一个整数,表示乘积的最大值,由于答案可能很大,请将答案对109+7取模后输出。

Sample Input

2
2
5

Sample Output

2
6

HINT

2=2
5=2+3

思路: 找找规律就知道了, 有3就放3,没3就放2。。。算是个结论把

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int Mod = 1e9 + 7;
typedef long long ll;
ll cal(int x)
{
    ll ans = 1, p = 3;
    while(x)
    {
        if(x&1) ans = (ans*p) % Mod;
        p = (p*p)%Mod;
        x >>= 1;
    }
    return ans;
}
int main()
{
    int t, n;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        int k = n / 3;
        ll ans = 0;
        if(n == 1)
        {
            printf("1\n");
            continue;
        }
        if(n == 0)
        {
            printf("0\n");
            continue;
        }
        if(n%3 == 1)
        {
            ans += cal(k-1);
            ans *= (ll)4;
            ans %= Mod;
        }
        else if(n%3 == 2)
            ans += (cal(k)*(ll)2)%Mod;
        else
            ans += cal(k)%Mod;
        printf("%lld\n", ans);
    }
    return 0;
}



Problem K: qwb与小数

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 451   Solved: 99
[ Submit][ Status][ Web Board]

Description

qwb遇到了一个问题:将分数a/b化为小数后,小数点后第n位的数字是多少?

做了那么多题,我已经不指望你能够帮上他了。。。

Input

多组测试数据,处理到文件结束。(测试数据<=100000组)

每组测试例包含三个整数a,b,n,相邻两个数之间用单个空格隔开,其中0 <= a <1e9,0 < b < 1e9,1 <= n < 1e9。

Output

对于每组数据,输出a/b的第n位数,占一行。

Sample Input

1 2 1
1 2 2

Sample Output

5
0


思路: 知道题解很简单。。 其实写出公式应该可以想出来的。。 小数点后面的第1位由  a%b*10/b 得到。第n位的话 就是 a%b*10%b*10%b....*10/b;你会发现a,还有每个10后面都有mod b。。可以变成a*10*10*10*10.....%b,这样就很熟习了把。。。快速幂。。,求出第n-1位,第n-1位除的余数乘10再除以b就是第n位的数,
三个公式差不多,第一个公式a%b*10%b*10%b....*10/b来的、、
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef __int128 ll;
ll a, b, n;
ll quick_mod(ll x, ll t, ll Mod)
{
    ll ans = a%Mod;  // a一定要mod b
    ll ans = 1;
    while(t)
    {
        if(t&1) ans = (ans*x)%Mod;
        x = x*x%Mod;
        t >>= 1;
    }
    return ans;
}
int main()
{
    while(~scanf("%lld%lld%lld", &a, &b, &n))
    {
        printf("%lld\n", quick_mod((ll)10, n-1, b)*10/b);
//        printf("%lld\n", a*qmod((ll)10, n-1, b)%b*10/b);
//        printf("%lld\n",  a*quick_mod((ll)10,n,(ll)10*b)/b%10);

    }
    return 0;
}

Problem J: qwb又偷懒了

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 697   Solved: 115
[ Submit][ Status][ Web Board]

Description

qwb最近在做一个群众收入统计。ta非常懒,以至于忘记了今天领导要来视察。所以急忙催下属去做统计。
在接下来长度为n的时间里,每个单位时间都有事情发生,可能会发下以下两种事件:

1)下属递交了一份调查报告,由于太匆忙,上面只有一个整数x,代表一个居民的收入。
2)领导来视察了,领导会来询问,收入在区间[l,r]内的居民的平均收入,qwb需要给出回答。

qwb非常讨厌小数,所以qwb上报时都会省略小数部分。如果上报时统计的人数为0,qwb就暴露了他偷懒的事情,他就会zhizhiwuwu。

Input

多组测试数据,处理到文件末尾。

每组测试数据的第一行为一个正整数n(0<=100000),确保所有的n的和不超过300000

接下来n行,
当第一个数为0时,代表操作1,后面跟着一个整数x(0<=x<=1000000),意义如题目所述。
当第一个数为1时,代表操作2,后面跟着两个整数l,r(0<=l<=r<=1000000),意义如题目描述。

Output

对于每一个领导的询问,给出一个回答,如果统计区间的人数为零,则输出"zhizhiwuwu"。(不带引号)
每个测试例之后输出一个空行。

Sample Input

3
0 1
0 3
1 1 3
2
0 1
1 2 2

Sample Output

2

zhizhiwuwu

HINT


输入输出包含大规模数据,建议使用scanf,printf.



样例1中,收入为1的居民有一个,收入为3的居民有1个,所以收入在1-3范围内的居民有2个,总收入是4,4/2=2

思路:tm这题比赛里读错题了。。 怪不得显示样例都re。。 理解题意后蛮简单的, 两棵树, 一个存人数, 一个存收入。。树状数组写写就好了,坑点在收入为0的情况,可以记录一下0的个数,或者直接平移1。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6 + 5;
typedef long long ll;
ll num[maxn], sum[maxn];
void update(int x, ll val, int cmd)
{
    while(x <= maxn)
    {
        if(cmd) num[x] += val;
        else sum[x] += val;
        x += x & -x;
    }
}
ll query(int x, int cmd)
{
    ll ans = 0;
    while(x >= 1)
    {
        if(cmd) ans += num[x];
        else ans += sum[x];
        x -= x & -x;
    }
    return ans;
}
int main()
{
    int n, cmd, l, r, x;
    while(~scanf("%d", &n))
    {
        memset(num, 0, sizeof(num));
        memset(sum, 0, sizeof(sum));
        while(n--)
        {
            scanf("%d", &cmd);
            if(cmd == 0)
            {
                scanf("%d", &x);
                update(x+1, 1, 1);  //整体右移
                update(x+1, x, 0);
            }
            else
            {
                scanf("%d%d", &l, &r);
                l++, r++; //整体 右移
                if(query(r, 1)-query(l-1, 1) == 0)
                    printf("zhizhiwuwu\n");
                else
                    printf("%lld\n", (query(r, 0)-query(l-1, 0))/(query(r, 1)-query(l-1, 1)));
            }
        }
        puts("");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值