第九届福建省大学生程序设计竞赛部分题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39942341/article/details/82053745

比赛就这样过去了,A2题水了个铜奖第三回来= =感觉能A3拿个银奖的

目录

A:Uint47 calculator

D:Number theory  

E:Traffic jam 

F:IoU 

G:Chosen by god    

I:Mind control      


扫描二维码关注公众号,回复: 4699321 查看本文章

A:Uint47 calculator

模拟47位计算,当时我没想起来快速乘,结果用java大数,报了一个我见都没见过的re,后来好久以后,问了工作人员,然后才重判A了,其实真的就是个水题,注意乘法会爆longlong,用快速乘就可以了

#include<iostream>
#include<string>
#include<map>
using namespace std;
typedef long long ll;
const ll mod = 1LL << 47;
ll quick_mul(ll a, ll b) {
	ll ans = 0;
	while (b) {
		if (b & 1)ans = (ans + a) % mod;
		a = (a + a) % mod;
		b >>= 1;
	}
	return ans;
}
int main() {
	string a, b, c;
	ll t;
	map<string, ll> m;
	while (cin >> a >> b) {
		if (a == "def") {
			cin >> t;
			m[b] = t;
		}
		else {
			cin >> c;
			if (a == "add")m[b] = (m[b] + m[c]) % mod;
			else if (a == "sub")m[b] = (m[b] - m[c] + mod) % mod;
			else if (a == "mul")m[b] = quick_mul(m[b], m[c]);
			else if (a == "div")m[b] = m[b] / m[c];
			else m[b] %= m[c];
		}
		cout << b << " = " << m[b] << endl;
	}
	return 0;
}

D:Number theory  

这题其实如果模一个质数就变成水题了,这题其实是一个线段树的点修改+区间查询,M操作可以看成把i节点修改成 yi,N操作可以看作吧di节点修改成1,然后每次的乘积就是区间1-10的乘积,如果看出来是线段树,其实只是个模板题

#include<iostream>
#include<cstring>
typedef long long ll;
const int N = 400005;
ll mul[N], M;
void build(int rt, int L, int R) {
	if (L == R) {
		mul[rt] = 1;
		return;
	}
	int mid = (L + R) >> 1;
	build(rt << 1, L, mid);
	build(rt << 1 | 1, mid + 1, R);
	mul[rt] = mul[rt << 1] * mul[rt << 1 | 1] % M;
}
void update(int rt, int L, int R, const int &pos, const int &val) {
	if (L == R) {
		mul[rt] = val;
		return;
	}
	int mid = (L + R) >> 1;
	if (pos <= mid)update(rt << 1, L, mid, pos, val);
	else update(rt << 1 | 1, mid + 1, R, pos, val);
	mul[rt] = mul[rt << 1] * mul[rt << 1 | 1] % M;
}
int main() {
	int T, Q, x;
	scanf("%d", &T);
	char s[2];
	while (T--) {
		scanf("%d%lld", &Q, &M);
		build(1, 1, Q);
		for (int i = 1; i <= Q; ++i) {
			scanf("%s%d", s, &x);
			if (s[0] == 'M')update(1, 1, Q, i, x);
			else update(1, 1, Q, x, 1);
			printf("%lld\n", mul[1]);
		}
	}
	return 0;
}

E:Traffic jam 

这题其实是一个最短路径,注意松弛不再是if(dis[v]>dis[now]+edge[now][i].second)了,要看能不能走(x=dis[now]/a[now],能走的话x%2==0),能走才是dis[now],不能的话,dis[now]要改成(x+1)*a[now],其他的就是正常的最短路径,要注意a[now]可以为0,这时候不能除,不知为什么当时这题没有A出来

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int N = 1001, inf = 0x3f3f3f3f;
vector<pair<int, int> > edge[N];
int dis[N], a[N], s, t;
void dijkstra() {
	priority_queue<pair<int, int> >q;
	dis[s] = 0;
	q.push(make_pair(0, s));
	while (!q.empty()) {
		int now = q.top().second;
		int cost = dis[now];
		if (a[now]) {
			int x = dis[now] / a[now];
			if (x & 1)cost = (x + 1)*a[now];
		}
		if (now == t)return;
		q.pop();
		for (int i = 0; i < edge[now].size(); ++i) {
			int v = edge[now][i].first;
			if (dis[v] > cost + edge[now][i].second) {
				dis[v] = cost + edge[now][i].second;
				q.push(make_pair(-dis[v], v));
			}
		}
	}
}
int main() {
	int T, n, m, x, y, z;
	scanf("%d", &T);
	while (T--) {
		for (int i = 0; i < N; ++i)edge[i].clear();
		memset(dis, inf, sizeof(dis));
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
		for (int i = 0; i < m; ++i) {
			scanf("%d%d%d", &x, &y, &z);
			edge[x].push_back(make_pair(y, z));
			edge[y].push_back(make_pair(x, z));
		}
		scanf("%d%d", &s, &t);
		dijkstra();
		printf("%d\n", dis[t]);
	}
	return 0;
}

F:IoU 

这题其实是最简单的一个题目,分别求出长和宽就可以了

#include<iostream>
typedef long long ll;
ll min(const ll &a, const ll &b) { return a <= b ? a : b; }
ll max(const ll &a, const ll &b) { return a >= b ? a : b; }
int main() {
	int x1, y1, w1, h1, x2, y2, w2, h2, T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d%d%d%d%d%d", &x1, &y1, &w1, &h1, &x2, &y2, &w2, &h2);
		ll overlap = 0, unionArea = w1 * h1 + w2 * h2;
		if (min(x1 + w1, x2 + w2) > max(x1, x2) && min(y1 + h1, y2 + h2) > max(y1, y2))
			overlap = (min(x1 + w1, x2 + w2) - max(x1, x2))*(min(y1 + h1, y2 + h2) - max(y1, y2));
		unionArea -= overlap;
		if (!unionArea)printf("0.00\n");
		else printf("%.2lf\n", 1.0*overlap / unionArea);
	}
	return 0;
}

G:Chosen by god    

这个奥数飞弹很骚,我当时根本没看出来,导致没看懂题目,其实就说攻击分裂成n份,每次攻击都是随机的,

那么也就是说n<m时为0,否则答案是\frac{\sum_{i=m}^{n}\binom{n}{i}}{2^{n}}

这题用杨辉三角+后缀和+费马小定理求逆元

#include<iostream>
typedef long long ll;
const int N = 1001, mod = 1e9 + 7;
ll c[N][N] = { 1 }, inv[N] = { 1 };
ll quickPow(ll a, ll b) {
	ll ans = 1;
	while (b) {
		if (b & 1)ans = ans * a%mod;
		a = a * a%mod;
		b >>= 1;
	}
	return ans;
}
int main() {
	int T, n, m;
	ll t = 2;
	for (int i = 1; i < N; ++i) {
		inv[i] = quickPow(t, mod - 2);
		t = t * 2 % mod;
	}
	for (int i = 1; i < N; ++i)
		for (int j = 0; j <= i; ++j)c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	for (int i = 1; i < N; ++i)
		for (int j = i - 1; j >= 0; --j)c[i][j] = (c[i][j] + c[i][j + 1]) % mod;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		if (m == 0)printf("1\n");
		else printf("%lld\n", c[n][m] * inv[n] % mod);
	}
	return 0;
}

I:Mind control      

这题其实期望很好推\frac{\sum_{i=m}^{n}i\binom{i-1}{m-1}}{\binom{n}{m}},但是要化简,当时不会化

\frac{\sum_{i=m}^{n}i\binom{i-1}{m-1}}{\binom{n}{m}} \\=\frac{\sum_{i=m}^{n}\frac{i*(i-1)!}{(m-1)!(m-i)!}}{\binom{n}{m}} \\=\frac{\sum_{i=m}^{n}\frac{m*i!}{m!(m-i)!}}{\binom{n}{m}} \\=\frac{\sum_{i=m}^{n}m\binom{i}{m}}{\binom{n}{m}} \\=\frac{m*\binom{n+1}{m+1}}{\binom{n}{m}} \\ =\frac{m*(n+1)}{m+1}

m+1就有逆元了,用逆元递推的,不然会wa(不知道为什么)

#include<iostream>
typedef long long ll;
const int mod = 1e9 + 7, N = 1e6 + 5;
ll inv[N] = { 0,1 };
int main(){
	for (int i = 2; i < N; i++)inv[i] = (mod - mod / i)*inv[mod%i] % mod;
	int T, n, m;
	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &m);
		printf("%lld\n", n <= m ? n : 1LL * m*(n + 1) % mod*inv[m + 1] % mod);
	}
	return 0;
}

剩下的题目会了再说吧

猜你喜欢

转载自blog.csdn.net/qq_39942341/article/details/82053745