比赛链接: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();
}
}