2019 China Collegiate Programming Contest Qinhuangdao Onsite
Contest Info
[Practice Link](https://codeforces.com/gym/102361)
Solved | A | B | C | D | E | F | G | H | I | J | K | L |
---|---|---|---|---|---|---|---|---|---|---|---|---|
5/12 | O | - | - | O | - | O | - | - | - | O | Ø | - |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. Angle Beats
题意:
给出\(n\)个点,每次询问再给出一个点,询问\(n\)个点中与当前这个点构成直角三角形的方案数。
D. Decimal
题意:
给出一个\(n\),判断\(\frac{1}{n}\)是否是一个无限循环小数。
思路:
如果\(n\)的质因子分解是\(2^x5^y\)那么不是无限循环
代码:
view code
#include <bits/stdc++.h>
#define debug(...) { printf("# "); printf(__VA_ARGS__); puts(""); }
#define fi first
#define se second
#define endl "\n"
using namespace std;
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair <int, int>;
using pLL = pair <ll, ll>;
constexpr int mod = 1e9 + 7;
template <class T1, class T2> inline void chadd(T1 &x, T2 y) { x += y; while (x >= mod) x -= mod; while (x < 0) x += mod; }
template <class T1, class T2> inline void chmax(T1 &x, T2 y) { if (x < y) x = y; }
template <class T1, class T2> inline void chmin(T1 &x, T2 y) { if (x > y) x = y; }
inline int rd() { int x; cin >> x; return x; }
template <class T> inline void rd(T &x) { cin >> x; }
template <class T> inline void rd(vector <T> &vec) { for (auto &it : vec) cin >> it; }
inline void pt() { cout << endl; }
template <class T, class... Ts> void pt(const T& arg, const Ts&... args) { cout << arg << " "; pt(args...); }
template <class T> inline void pt(const T &s) { cout << s << "\n"; }
template <class T> inline void pt(const vector <T> &vec) { for (auto &it : vec) cout << it << " "; cout << endl; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline ll qpow(ll base, ll n) { ll res = 1; while (n) { if (n & 1) res = res * base % mod; base = base * base % mod; n >>= 1; } return res; }
constexpr int N = 1e5 + 10;
int n;
void run() {
cin >> n;
while (n % 2 == 0) n /= 2;
while (n % 5 == 0) n /= 5;
if (n == 1) pt("No");
else pt("Yes");
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T; cin >> _T;
while (_T--) run();
return 0;
}
F. Forest Program
题意:
给出一个无向图,每条边最多存在于一个环中,问多少种移除边的方案使得剩下的每一个连通块都是一棵树
思路:
一个环,这个环上至少要切一条边,其他边任意切不切。
注意刚开始给出的可能不是连通图。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5 + 10;
const ll mod = 998244353;
int n, m, bit[N];
vector <vector<int>> G;
int dep[N], tot, vis[N], Insta[N]; ll res;
void dfs(int u, int pre) {
vis[u] = 1;
Insta[u] = 1;
for (auto &v : G[u]) if (v != pre) {
if (Insta[v]) {
tot -= dep[u] - dep[v] + 1;
res = 1ll * res * (bit[dep[u] - dep[v] + 1] - 1) % mod;
res %= mod;
}
if (!vis[v]) {
dep[v] = dep[u] + 1;
dfs(v, u);
}
}
Insta[u] = 0;
}
int main() {
bit[0] = 1;
for (int i = 1; i < N; ++i) bit[i] = 1ll * bit[i - 1] * 2 % mod;
while (scanf("%d%d", &n, &m) != EOF) {
G.clear(); G.resize(n + 1);
for (int i = 1; i <= n; ++i) dep[i] = vis[i] = Insta[i] = 0;
for (int i = 1, u, v; i <= m; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
tot = m; res = 1;
for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i, i);
res = 1ll * res * bit[tot] % mod;
res = (res + mod) % mod;
printf("%lld\n", res);
}
return 0;
}
I. Invoker
题意:
每种技能需要三种属性,三个属性被按下就可以触发,而顺序不重要。
但是每次只能保留三个属性,按下第四个的时候第一个会被移除。
现在给出一个需要使用的技能表,询问怎样按键使得按顺序出发这些技能并且按下的次数最少。
思路:
考虑每种技能最多只有六种排列状态,那么\(f[i][S]\)表示前\(i\)个技能,当前排列状态为\(S\)的最少按键数。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
char s[N]; int n;
map <string, int> f[2];
map <char, string> mp;
int main() {
mp['Y'] = "QQQ";
mp['V'] = "QQW";
mp['G'] = "EQQ";
mp['C'] = "WWW";
mp['X'] = "QWW";
mp['Z'] = "EWW";
mp['T'] = "EEE";
mp['F'] = "EEQ";
mp['D'] = "EEW";
mp['B'] = "EQW";
while (scanf("%s", s + 1) != EOF) {
f[0].clear(), f[1].clear();
int n = strlen(s + 1);
for (int i = 1; i <= n; ++i) {
int p = i & 1;
if (i > 1 && s[i] == s[i - 1]) {
f[p] = f[p ^ 1];
continue;
}
f[p].clear();
string s1 = mp[s[i]];
// cout << s1 << endl;
do {
if (i == 1) {
f[p][s1] = 3;
continue;
}
f[p][s1] = 1e9;
for (auto &it : f[p ^ 1]) {
string s2 = it.first;
// cout << s1 << " " << s2 << endl;
int w = it.second;
if (s2[1] == s1[0] && s2[2] == s1[1]) {
f[p][s1] = min(f[p][s1], w + 1);
} else if (s2[2] == s1[0]) {
f[p][s1] = min(f[p][s1], w + 2);
} else {
f[p][s1] = min(f[p][s1], w + 3);
}
}
}while (next_permutation(s1.begin(), s1.end()));
}
int res = 1e9;
for (auto &it : f[n & 1]) {
// cout << it.first << " " << it.second << endl;
res = min(res, it.second);
}
printf("%d\n", res + n);
}
return 0;
}
J. MUV LUV EXTRA
题意:
给出一个小数,询问小数部分后缀中的\(a \cdot p - b \cdot l\)的最大值。
\(a, b\)是参数,\(p\)表示循环节长度,\(l\)表示后缀长度
思路:
将字符串翻转,用\(kmp\)找循环节即可。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
ll a, b;
char s[N], t[N]; int n;
void get() {
int i;
for (i = 1; s[i]; ++i) {
if (s[i] == '.') break;
}
n = 0;
for (++i; s[i]; ++i) {
t[n++] = s[i];
}
t[n] = 0;
}
struct KMP {
int Next[N];
//下标从0开始
void get_Next(char *s) {
int len = strlen(s);
int i, j;
j = Next[0] = -1;
i = 0;
while (i < len) {
while (-1 != j && s[i] != s[j]) j = Next[j];
Next[++i] = ++j;
}
}
}kmp;
int main() {
while (scanf("%lld%lld", &a, &b) != EOF) {
scanf("%s", s + 1);
get();
reverse(t, t + n); t[n] = 0;
kmp.get_Next(t);
ll res = a - b;
for (int i = 1; i < n; ++i) {
ll p = i + 1;
// cout << i << " " << kmp.Next[i + 1] << endl;
ll l = p - kmp.Next[i + 1];
res = max(res, a * p - b * l);
}
printf("%lld\n", res);
}
return 0;
}