2023年湖北省大学生程序设计竞赛题解

比赛链接:2023 Hubei Provincial Collegiate Programming Contest

C. Darkness I(思维+构造)

只要最上面一行和最左边一列填满就可以使整个矩阵填满了

所以怎么能让最上面一行和最左边一列填满呢?隔着填就好啦,所以输出 (n + m + 1) / 2

#include <bits/stdc++.h>
 
using namespace std;
 
#define int long long
using i64 = long long;
 
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
 
const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
 
void solve()
{
	int n, m;
	cin >> n >> m;
	int ans = (m + n + 1) / 2;
	cout << ans << '\n';
}
 
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
 
	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

F. Inverse Manacher(思维+构造)

给出 2n + 2 个数,首先我们可以确定前4个位置一定是 &|a| ,然后其实不用关注字母位的数字,只需要关注 | 位的数字即可,如果数字是 1,说明它左右两边的字母不一样,如果大于 1,说明左右两边字母一样,根据这个填上右边的字母就好了

#include <bits/stdc++.h>
 
using namespace std;
 
#define int long long
using i64 = long long;
 
typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;
 
const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;
 
void solve()
{
	int n;
	cin >> n;
	vector<char> s(2 * n + 3);
	s[1] = '&', s[2] = '|', s[3] = 'a';
	vector<int> a(2 * n + 3);
	for (int i = 1; i <= 2 * n + 2; i ++ ) cin >> a[i];
	for (int i = 4; i <= 2 * n + 2; i ++ )
	{
		if (i % 2 == 0)
		{
			s[i] = '|';
			if (a[i] > 1) s[i + 1] = s[i - 1];
			else
			{
				if (s[i - 1] == 'a') s[i + 1] = 'b';
				else s[i + 1] = 'a';
			}
		}
	}
	for (int i = 3; i <= 2 * n + 2; i ++ )
	{
		if (i & 1) cout << s[i];
	}
	cout << '\n';
}
 
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
 
	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

H. Binary Craziness(暴力)

把相同的数值放在一起算就行了,一个一个算肯定会t的

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, m;
	cin >> n >> m;
	vector<int> ind(n + 1);
	for (int i = 0; i < m; i ++ )
	{
		int u, v;
		cin >> u >> v;
		ind[u] ++ ;
		ind[v] ++ ;
	}
	map<int, int> mp;
	for (int i = 1; i <= n; i ++ ) mp[ind[i]] ++ ;
	vector<PII> v;
	for (auto t : mp) v.push_back(t);
	int ans = 0;
	for (int i = 0; i < v.size(); i ++ )
	{
		for (int j = i + 1; j < v.size(); j ++ )
		{
			ans = (ans + (v[i].second * v[j].second) % mod * ((v[i].first & v[j].first) % mod * (v[i].first | v[j].first) % mod * (v[i].first ^ v[j].first) % mod) % mod) % mod;
		}
	}
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

I. Step(exgcd)

设所有环长度的最小公倍数是 t ,答案的最小天数是 k,那么一共走的步数就是 ( 1 + k ) k 2 \frac{(1+k)k}{2} 2(1+k)k,如果要让所有马都回到 1 的位置,就要让 ( 1 + k ) k 2 \frac{(1+k)k}{2} 2(1+k)k 是 t 的倍数,也就是让 ( k + 1 ) k (k+1)k (k+1)k 2 t 2t 2t 的倍数

2 t = a × b 2t=a\times b 2t=a×b,让 k 是 a 的倍数,k + 1 是 b 的倍数,即 a x = k ,   b y = k + 1 ax=k,\ by=k+1 ax=k, by=k+1,得到 a x + 1 = b y ax+1=by ax+1=by,也就是 a ( − x ) + b y = 1 a(-x)+by=1 a(x)+by=1,如果我们知道了 a 和 b(且 a 和 b 要互质),这个式子就可以用 exgcd 解出来了

我们可以对 2 t 2t 2t 进行分解,分解出所有的质数,将不同的质数种类分配给 a 和 b(用二进制分配),然后解出 x 和 y,取最小的 a x ax ax 作为答案

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 5e7 + 10;
const int maxn = 1e7 + 10;
const int mod = 80112002;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

bool isprime[maxn]; // isprime[i]表示i是不是素数
int prime[maxn]; // 现在已经筛出的素数列表
int n; // 上限,即筛出<=n的素数
int cnt; // 已经筛出的素数个数

void euler()
{
    memset(isprime, true, sizeof(isprime)); // 先全部标记为素数
    isprime[1] = false; // 1不是素数
    for(int i = 2; i < maxn; ++i) // i从2循环到n(外层循环)
    {
        if(isprime[i]) prime[++cnt] = i;
        // 如果i没有被前面的数筛掉,则i是素数
        for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
        // 筛掉i的素数倍,即i的prime[j]倍
        // j循环枚举现在已经筛出的素数(内层循环)
        {
            isprime[i * prime[j]] = false;
            // 倍数标记为合数,也就是i用prime[j]把i * prime[j]筛掉了
            if(i % prime[j] == 0) break;
            // 最神奇的一句话,如果i整除prime[j],退出循环
            // 这样可以保证线性的时间复杂度
        }
    }
}

int exgcd(int a, int b, int &x, int &y) // 扩展欧几里得
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int r = exgcd(b, a % b, x, y);
    int temp = y;
    y = x - (a / b) * y;
    x = temp;
    return r; // 得到a b的最大公因数
}

void solve()
{
	euler();
	int n;
	cin >> n;
	map<int, int> mp;
	vector<int> v;
	int res = 1;
	for (int i = 1; i <= n; i ++ )
	{
		int x; cin >> x;
		res = lcm(res, x);
	}
	int tmp = 2 * res;
	for (int j = 1; j <= cnt; j ++ )
	{
		if (tmp % prime[j]) continue;
		mp[prime[j]] = 1;
		v.push_back(prime[j]);
		while (tmp % prime[j] == 0)
		{
			mp[prime[j]] *= prime[j];
			tmp /= prime[j];
		}
		if (tmp == 1) break;
	}
	int ans = INF;
	for (int i = 0; i < (1ll << v.size()); i ++ )
	{
		int a = 1;
		for (int j = 0; j < v.size(); j ++ )
		{
			if ((i >> j) & 1) a *= mp[v[j]];
		}
		int b = 2 * res / a;
		int x, y, d = exgcd(a, b, x, y);
		x = -x;
		x = (x % (b / d) + (b / d)) % (b / d);
		if (a * x) ans = min(ans, a * x);
	}
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

J. Expansion(前缀和)

首先预处理一下前缀和 pre[i] ,在第 i 个位置加的值就是 pre[i]

然后再处理一下每个位置前面的最大值

然后从前往后遍历,到某一个位置,如果值小于0,那么就加这个位置之前的最大值,直到加到大于等于 0 为止

要注意预先判断一下第一个非零 pre 的值一定要大于 0,pre[n] 一定要小于 0

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	vector<int> pre(n + 1);
	for (int i = 1; i <= n; i ++ ) pre[i] = pre[i - 1] + a[i];
	if (pre[n] < 0 || pre[1] < 0)
	{
		cout << -1 << '\n';
		return;
	}
	int maxx = 1;
	vector<int> maxn(n + 1);
	for (int i = 2; i <= n; i ++ )
	{
		maxn[i] = maxx;
		if (pre[i] > pre[maxx]) maxx = i;
	}
	int ans = 1, have = pre[1];
	for (int i = 2; i <= n; i ++ )
	{
		if (have + pre[i] < 0)
		{
			if (pre[maxn[i]] <= 0)
			{
				cout << -1 << '\n';
				return;
			}
			int cnt = (-have - pre[i]) / pre[maxn[i]];
			if ((-have - pre[i]) % pre[maxn[i]] != 0) cnt ++ ;
			ans += cnt;
			have += cnt * pre[maxn[i]];
		}
		have += pre[i];
		ans ++ ;
	}
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

K. Dice Game(概率)

做完这题终于知道逆元怎么用了(

感性理解一下,如果第一个人的值是 x,其他人必须要比 x 大才能让第一个人输(如果也是 x 的话相当于没作用),所以情况总数是 m - 1 ,能赢的情况是 m - x,所以 每一个人赢了第一个人的概率就是 (m - x) / (m - 1) ,n 个人就是 n 次方

需要注意一下取模的情况

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

int pow(int a, int n, int p) // 快速幂取模
{
    int ans = 1;
    while (n)
    {
        if (n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}

int niYuan(int a, int b)	//费马小定理求逆元
{
    return pow(a, b - 2, b);
}

void solve()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= m; i ++ )
	{
		int ans = pow((m - i) * niYuan((m - 1), mod) % mod, n, mod);
		cout << ans << ' ';
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

M. Different Billing(暴力)

枚举 a,然后解方程求 b c

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 1e6 + 10;
const int maxn = 1e6 + 10;
const int mod = 998244353;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int x, y;
	cin >> x >> y;
	if (y % 500 != 0)
	{
		cout << -1 << '\n';
		return;
	}
	int tmp = y / 500;
	int a, b, c;
	for (int i = 0; i <= x; i ++ )
	{
		int tmp2 = 2 * x - 2 * i;
		if (tmp2 < 0)
		{
			cout << -1 << '\n';
			return;
		}
		int tmp3 = tmp - tmp2;
		if (tmp3 % 3 == 0)
		{
			c = tmp3 / 3;
			b = x - i - c;
			a = i;
			if (c >= 0 && a >= 0 && b >= 0 && a + b + c == x && 1000 * b + 2500 * c == y)
			{
				cout << a << ' ' << b << ' ' << c << '\n';
				return;
			} 
		}
	}
	cout << -1 << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值