2017年第0届浙江工业大学之江学院程序设计竞赛决赛部分题解

A

二分 + 容斥

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
LL Gcd(LL a, LL b){
	return b == 0 ? a : Gcd(b, a % b);
}
LL Lcm(LL a, LL b){
	return a / Gcd(a, b) * b;
}
LL Check(LL a, LL b, LL c, LL n){
	LL ans;
	ans = n / a + n / b + n / c;
	ans = ans - n / (Lcm(a, b)) - n / (Lcm(a, c)) - n / (Lcm(b, c));
	ans = ans + n / (Lcm(a, Lcm(b, c)));
	return ans;
}

int main(){
	LL a, b, c, n;
	while(scanf("%lld%lld%lld%lld", &a, &b, &c, &n) != EOF){
		LL ans;
		LL minx = min(a, min(b, c));
		if(n < minx){
			printf("%lld\n", n);
			continue;
		}
		LL l = n, r = 1e18;
		while(l <= r){
			LL mid = (l + r) / 2;
			LL tmp = Check(a, b, c, mid);
			if(mid - tmp >= n){
				ans = mid;
				r = mid - 1;
			}else{
				l = mid + 1;
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}



注意只有y坐标是可以以倍数形式去走得,这里可以考虑素数的线性晒法,然后更新答案的时候有个记录就行

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
const LL INF = 1e17;
LL dp[21][10005];
LL maxn[21][10005];
int main(){
	int t;	scanf("%d", &t);
	while(t--){
		int n, m;	scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= m; ++j){
				scanf("%lld", &dp[i][j]);
				maxn[i][j] = -INF;
			}
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= m; ++j){
				if(i == 1 && j == 1){
					for(int k = j + j; k <= m; k += j)
						maxn[i][k] = max(maxn[i][k], dp[i][j]);
					continue;
				}
				LL t = maxn[i][j];
				if(i - 1 >= 1)	t = max(t, dp[i - 1][j]);
				if(j - 1 >= 1)	t = max(t, dp[i][j - 1]);
				dp[i][j] += t;
				for(int k = j + j; k <= m; k += j)
					maxn[i][k] = max(maxn[i][k], dp[i][j]);
			}
		printf("%lld\n", dp[n][m]);
	}
	return 0;
}


C

实际上是个组合数学题,首先每天给k个题,还剩下n = n - m * k, 实际上要讨论的问题就是把n分解成m个部分,你可以把这n看成n个1,然后分成m个部分,但是有些部分可以为0,所以实际求得组合数是C(n + m - 1, m - 1),然后就是关于如何计算末尾有多少个0, 可以参考:传送门 , 然后把组合数全部化成阶乘的形式就可以算结果了

推荐一道类似的题:传送门

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
LL Cal(LL n, LL k){
	LL cnt = 0;
	while(n > 0){
		cnt += n / k;
		n /= k;
	}
	return cnt;
}

int main(){
	LL n, m, k;
	while(scanf("%lld%lld%lld", &m, &n, &k) != EOF){
		n = n - m * k;
		if(n <= 0){
			puts("0");
			continue;
		}
		LL a = Cal(n + m - 1, 5) - Cal(n, 5) - Cal(m - 1, 5);
		LL b = Cal(n + m - 1, 2) - Cal(n, 2) - Cal(m - 1, 2);
		printf("%lld\n", min(a, b));
	}
	return 0;
}


D

推出公式之后就是个矩阵快速幂了、

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
const int MOD = 1e8;
struct Rec{
	LL mar[3][3];
	Rec(){
		mst(mar, 0);
	}
	Rec operator * (const Rec &d)const{
		Rec tmp;
		mst(tmp.mar, 0);
		for(int i = 0; i < 2; ++i)
			for(int j = 0; j < 2; ++j)
				for(int k = 0; k < 2; ++k){
					tmp.mar[i][j] = (tmp.mar[i][j] + (mar[i][k] * d.mar[k][j] % MOD + MOD) % MOD + MOD) % MOD;
				}
		return tmp;
	}
}ans, f;
void Quick(LL n, LL f1){
	mst(ans.mar, 0);
	ans.mar[0][0] = 3, ans.mar[0][1] = 1;
	ans.mar[1][1] = 1;
	mst(f.mar, 0);
	f.mar[0][0] = f.mar[1][1] = 1;
	while(n > 0){
		if(n & 1)	f = f * ans;
		n >>= 1;
		ans = ans * ans;
	}
}

int main(){
	LL a, b, n;
	while(scanf("%lld%lld%lld", &a, &b, &n) != EOF){
		a %= MOD;
		b %= MOD;
		LL f1 = (a + b) % MOD;
		Quick(n, f1);
		LL res = (f1 * f.mar[0][1] + f1) % MOD;
		printf("%lld\n", (res + MOD) % MOD);
	}
	return 0;
}


E

先把宝藏数量分成尽可能平均的两部分,然后求出这两部分分出的所有结果的差,然后枚举其中一个去二分找另外一个的最好匹配(也就是差值最小),有些小技巧

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 40;
double x[qq], y[qq];
double ans[1 << 19], cns[1 << 19];
int t1, t2;
void Solve(double *p, int n, int f){
	LL minx = 1e15;
	for(int i = 0; i < (1 << n); ++i){
		double sum1, sum2, sum;
		sum1 = sum2 = sum = 0.0;
		for(int j = 0; j < n; ++j){
			sum += p[j];
			if(i & (1 << j))	sum1 += p[j];
		}
		sum2 = sum - sum1;
		minx = abs(sum1 - sum2);
		if(f == 1){
			ans[t1++] = max(sum2, sum1) - min(sum2, sum1);
		}else{
			cns[t2++] = min(sum2, sum1) - max(sum2, sum1);
		}
	}
}

int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		int q1, q2;
		q1 = q2 = 0;
		t1 = t2 = 0;
		for(int i = 1; i <= n / 2; ++i)
			scanf("%lf", &x[q1++]);
		for(int i = n / 2 + 1; i <= n; ++i)
			scanf("%lf", &y[q2++]);
		if(n == 1){
			printf("%.2lf\n", y[0]);
			continue;
		}
		Solve(x, q1, 1);
		Solve(y, q2, 2);
		for(int i = 0; i < t2; ++i)
			cns[i] = -cns[i];
		sort(cns, cns + t2);
		cns[t2] = 1e15;
		double minx = 1e15;
		for(int i = 0; i < t1; ++i){
			int k = lower_bound(cns, cns + t2, ans[i]) - cns;
			double t = abs(ans[i] - cns[k]);
			if(k + 1 < t1)	t = min(t, abs(ans[i] - cns[k + 1]));
			if(k - 1 >= 0)	t = min(t, abs(ans[i] - cns[k - 1]));
			minx = min(minx, t);
		}
		printf("%.2lf\n", minx);
	}
	return 0;
}


F

NIM游戏的裸题、

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
LL num[qq];

int main(){
	int n;	
	while(scanf("%d", &n) != EOF){
		LL ans = 0;
		for(int i = 0; i < n; ++i){
			scanf("%lld", num + i);
			ans ^= num[i];
		}
		if(ans == 0){
			printf("0\n");
			continue;
		}
		int maxn = 0, cnt = 0;
		LL p = ans;
		while(p > 0){
			if(p & 1)	maxn = cnt;
			cnt++;
			p >>= 1;
		}
		int res = 0;
		for(int j = 0; j < n; ++j){
			if(num[j] & (1 << maxn))	res++;
		}
		printf("%d\n", res);
	}
	return 0;
}


G

参考:传送门

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
const int MOD = 1e9 + 7;
LL Quick(LL a, LL b){
	if(b <= 0)	return 1;
	LL ans = 1;
	while(b){
		if(b & 1)	ans = (ans * a) % MOD;
		a = (a * a) % MOD;
		b >>= 1;
	}
	return ans;
}


int main(){
	int t;	scanf("%d", &t);
	while(t--){
		int n;	scanf("%d", &n);
		if(n < 3){
			printf("%d\n", n);
			continue;
		}
		LL ans;
		if(n % 3 == 1)	ans = (Quick(3, n / 3 - 1) * 4) % MOD;
		else if(n % 3 == 2)	ans = (Quick(3, n / 3) * 2LL) % MOD;
		else	ans = Quick(3, n / 3);
		printf("%lld\n", ans);
	}
	return 0;
}


J

比赛的节奏没掌握好,这题也是道简单题,用两个树状数组维护一下收入和人数,因为收入有0的情况,不妨把整体向右平移一位

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 2e6 + 10;
LL mon[qq], peo[qq];

int lowbit(int x){
	return x & (-x);
}
void Update(LL *p, int x, int v){
	while(x < qq){
		p[x] += v;
		x += lowbit(x);
	}
}
LL Getsum(LL *p, int x){
	LL ans = 0;
	while(x > 0){
		ans += p[x];
		x -= lowbit(x);
	}
	return ans;
}

int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		mst(mon, 0), mst(peo, 0);
		int a, b, op;
		while(n--){
			scanf("%d", &op);
			if(op == 0){
				scanf("%d", &a);
				a++;
				Update(mon, a, a);
				Update(peo, a, 1);
			}else{
				scanf("%d%d", &a, &b);
				a++, b++;
				int k = Getsum(peo, b) - Getsum(peo, a - 1);
				if(k == 0){
					puts("zhizhiwuwu");
					continue;
				}
				LL money = Getsum(mon, b) - Getsum(mon, a - 1);
				printf("%lld\n", (money - k) / k);
			}
		}
		puts("");
	}
	return 0;
}



K

其实和普通的除法一样,小学奥数的举一反三? 

a/b 的第n位是多少取决于n-1位模b的结果,然后递归即可,这里数据较大,采用快速幂直接计算

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e3 + 5;
vector<int> vt[qq];
int num[10005];

LL Quick(LL a, LL b, LL MOD){
	LL ans = 1;
	while(b){
		if(b & 1)	ans = (ans * a) % MOD;
		a = (a * a) % MOD;
		b >>= 1;
	}
	return ans;
}

int main(){
	LL a, b, n;
	while(scanf("%lld%lld%lld", &a, &b, &n) != EOF){
		printf("%lld\n", ((a % b) * Quick(10LL, n - 1, b) % b) * 10 / b);
	}
	return 0;
}


M

dp[i][j] 代表n个节点有m个叶子节点的情况数量.

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;
const int MOD = 1e9 + 7;
LL dp[55][55];
LL Dfs(int n, int m){
	if(dp[n][m] != -1)	return dp[n][m];
	dp[n][m] = 0;
	for(int i = 0; i <= n - 1; ++i)
		for(int j = 0; j <= i && j <= m; ++j){
			(dp[n][m] += (Dfs(i, j) * Dfs(n - i - 1, m - j)) % MOD) %= MOD;
		}
	return dp[n][m];
}

int main(){
	LL n, m;
	mst(dp, -1);
	dp[0][0] = 1;
	dp[1][0] = 0;
	dp[1][1] = 1;
	while(scanf("%d%d", &n, &m) != EOF){
		if(dp[n][m] == -1)	Dfs(n, m);
		printf("%lld\n", dp[n][m]);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值