The 10th Jimei University Programming Contest
外校打星队伍,排名22/450,还算凑合吧。
A. A+B问题
直接枚举进制
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
void solve() {
string str;
vi a, b, s;
cin >> str;
for (auto c: str) {
if (c >= '0' and c <= '9') a.push_back(c - '0');
else a.push_back(c - 'A' + 10);
}
cin >> str;
for (auto c: str) {
if (c >= '0' and c <= '9') b.push_back(c - '0');
else b.push_back(c - 'A' + 10);
}
cin >> str;
for (auto c: str) {
if (c >= '0' and c <= '9') s.push_back(c - '0');
else s.push_back(c - 'A' + 10);
}
auto f = [](vi a, int x) {
if (*max_element(a.begin(), a.end()) >= x) return -1;
int ans = 0;
for (auto i: a)
ans = ans * x + i;
return ans;
};
for (int i = 2, A, B, S; i <= 16; i++) {
A = f(a, i);
if (A == -1) continue;
B = f(b, i);
if (B == -1) continue;
S = f(s, i);
if (S == -1) continue;
if (S == A + B) {
cout << i << "\n";
return;
}
}
}
int main() {
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
B. 看比赛
首先正反两遍最短路,这样就可以确定那些边一定是最短路上的边,删掉多余的方向和边就会形成一个有向无环图。
我们发现,在有向图上能一步到达 n 的点,对于这些点是先手必胜点。然后只能够走到先手必胜的点的点就是先手必败点,根据这个规律,我们把整张图反向,然后按照拓扑序根据这个规则去给点染色,最后判断 1 的属性即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e18;
void solve() {
int n, m;
cin >> n >> m;
vector<vector<pair<int, int>>> e(n + 1);
vector<vi> g(n + 1);
vector<array<int, 3>> edge(m);
for (auto &[u, v, w]: edge) {
cin >> u >> v >> w;
e[u].emplace_back(v, w);
e[v].emplace_back(u, w);
}
auto dij = [n, e](int x) {
vi dis(n + 1, inf);
vi vis(n + 1, 0);
dis[x] = 0ll;
priority_queue<pii, vector<pii>, greater<pii>> q;
q.emplace(0, x);
while (!q.empty()) {
auto [d, u] = q.top();
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto [v, w]: e[u]) {
if (dis[v] <= d + w)continue;
dis[v] = d + w;
q.emplace(dis[v], v);
}
}
return dis;
};
auto d1 = dij(1) , d2 = dij(n);
vi cnt(n+1) , f(n+1);
for( auto [ u , v , w ] : edge ){
if( d1[u] + w + d2[v] == d1[n] )
g[v].push_back(u) , cnt[u] ++;
else if ( d1[v] + w + d2[u] == d1[n] )
g[u].push_back(v) , cnt[v] ++;
}
queue<int> q;
q.push(n);
while( !q.empty() ){
int u = q.front();
q.pop();
for( auto v : g[u] ){
if( f[u] == 0 ) f[v] = 1;
cnt[v] --;
if( cnt[v] == 0 ) q.push(v);
}
}
if( f[1] ) cout << "Little M is the winner.\n";
else cout << "Little I is the winner.\n";
return ;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
C. 方格染色
#include <bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e18;
int32_t main() {
int n = 10, m = 10;
vector f(n + 1, vector(m + 1, vi(3, 0)));
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= i; j++) {
for (int k = 0; k < 3; k++) {
if (j == 0 and k > 0) continue;
for (int l = 0; l < 3; l++) {
if ((l & k) != 0) continue;
if( j - (k > 0) == 0 and l != 0 ) continue;
f[i][j][k] += f[i - 1][j - (k > 0)][l];
}
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
cout << accumulate(f[i][j].begin(), f[i][j].end(), 0ll) << " ";
}
cout << "\n";
}
return 0;
}
首先我写了一个这样的 dp 去打表,然后对于\((n,k)\)的答案,发现当\(n<k\)时一定无解。
为了方便令\(n=n-k+1\),发现递推式子\(f(n,k)=\frac{2\times k\times f(n-1,k)}{n-1}+f(n-2,k),f(0,k)=0,f(1,k)=2\)
这样子我们自然可以得到一个\(O(nk)\)的递推,但是我发现\(\sum k \le 5\times10^5\),那么\(k\)的种类数最多就是\(1,2,3,\cdots\)这样取,也就是\(\frac{k(k-1)}{2}\le 5\times10^5\),\(k\)最多大约是\(10^3\)中,这样的话配合记忆化就可以实现复杂度控制在\(O(10^8)\)这样
#include <bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 1e18;
const int N = 1e5 + 5;
const int mod = 998244353;
vector<vector<int>> f(N);
vi inv(N);
int power( int x , int y ){
int ans = 1;
while( y ){
if( y & 1) ans = ans * x % mod;
x = x * x % mod , y /= 2;
}
return ans;
}
void solve() {
int n, k;
cin >> n >> k;
if (n < k) {
cout << "0\n";
} else if (k == 0) {
cout << "1\n";
} else {
int t = n - k + 1;
if (f[k].empty()) f[k].push_back(0), f[k].push_back(2);
for (int i = f[k].size() - 1; i <= t; i++)
f[k].push_back((2 * k % mod * f[k][i] % mod * inv[i] % mod + f[k][i - 1]) % mod);
cout << f[k][t] << "\n";
}
return;
}
int32_t main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
inv[0] = -1;
for( int i = 1 ; i < N ; i ++ ) inv[i] = power( i , mod-2);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
E. 时间超限α
就是求一下前缀的最大值就好了。
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
int res = 0;
vector< vi > a(n+1);
for( int i = 1 , x ; i <= n ; i ++ ){
cin >> x;
a[i].resize( x + 1 );
for( int j = 1 ; j <= x ; j ++ ) cin >> a[i][j];
}
int m;
cin >> m;
for( int i = 1 ; i <= n ; i ++ ){
for( int j = 1 ; j <= min( m , (int)a[i].size()-1 ) ; j ++ )
res = max( res , a[i][j] );
}
cout << res << "\n";
return 0;
}
I. 期中考试
模拟一下就好了
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
set<int> x;
for (int i = 1, v; i <= 3; i++)
cin >> v, x.insert(v);
int res = 0;
int a0, a1, b0, b1;
cin >> a0 >> a1 >> b0 >> b1;
if (a0 == b0) {
for (int i = a1; i < b1; i++) {
if (x.count(i) == 0) res++;
}
} else {
for (int i = a1; i <= 7; i++) {
if (x.count(i) == 0) res++;
}
for (int i = 1; i < b1; i++) {
if (x.count(i) == 0) res++;
}
int cnt = 0;
for (int i = 1; i <= 7; i++) {
if (x.count(i) == 0) cnt++;
}
for (int i = a0 + 1; i < b0; i++)
res += cnt;
}
cout << res << "\n";
return 0;
}
L. 兄弟校问题
首先兄弟学校之间的关系用并查集来维护就好了。
剩下就是如何判断兄弟学校,首先\(O(n^2)\)的根据城市判断一下。
然后再\(O(nm)\)的根据关键字判一下,因为这里面的字符串不太长,我直接截取下来,转成小写字母丢进 set 就好了。
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
#define lowbit(x) ( x & - x )
struct dsu {
vi fa;
dsu(int n = 1){
fa = vi( n+1 , -1ll );
fa[0] = 0;
}
int getfa(int x) {
if (fa[x] < 0) return x;
return fa[x] = getfa(fa[x]);
}
void merge(int x, int y) {
x = getfa(x), y = getfa(y);
if (x == y) return;
if (fa[x] > fa[y]) swap(x, y);
fa[x] += fa[y], fa[y] = x;
}
int size(int x) {
x = getfa(x);
return -fa[x];
}
};
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
dsu d(n);
vector<set<string>> keyWords(n+1);
vector<string> city(n+1);
auto toLow = [](string s) {
for (auto &i: s)
if (i >= 'A' and i <= 'Z') i = i + 'a' - 'A';
return s;
};
for (int i = 1; i <= n; i++) {
string s , t = "";
cin >> s >> city[i];
s += '_';
for ( auto c: s) {
if (c == '_') {
keyWords[i].insert(toLow(t) );
t = "";
} else t += c;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (city[i] != city[j]) continue;
d.merge(i, j);
}
}
string key;
for (int i = 1, p, q; i <= m; i++) {
cin >> key;
for (p = 1; p <= n; p++)
if (keyWords[p].count(key)) break;
for (q = p + 1; q <= n; q++) {
if (keyWords[q].count(key)) d.merge(p, q);
}
}
for( int i = 1 ; i <= n ; i ++ )
cout << d.size(i) - 1 << "\n";
return 0;
}