模拟赛小结:2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)
比赛链接:传送门
本场我们队过的题感觉算法都挺简单的,不知道为啥做的时候感觉没有很顺利。
封榜后7题,罚时1015。第一次模拟赛金,虽然是北欧的区域赛,但还是有点开心的。
Problem B Best Relay Team 00:49 (+2) Solved by xk
排序后简单模拟一下题意即可。
xk说他要背个锅。
代码:
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#ifdef ONLINE_JUDGE
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#else
#define fast
#endif
#define forn(i, n) for(int i = 0; i < (n); i++)
#define forab(i, a, b) for(int i = (a); i <= (b); i++)
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 500 + 5;
string s[maxn];
db a[maxn], b[maxn];
int main()
{
int n;
fast;
cin >> n;
forn(i, n)
{
cin >> s[i] >> a[i] >> b[i];
}
vector<pair<db, int> > va, vb;
forn(i, n)
{
va.push_back(mp(a[i], i));
vb.push_back(mp(b[i], i));
}
sort(va.begin(), va.end());
sort(vb.begin(), vb.end());
db ans = 1e100;
int aid = 0;
int isin = 0;
forn(i, n)
{
db temp = a[i];
int in3 = 0;
forn(j, 3)
{
if(i == vb[j].second) {
in3 = 1;
continue;
}
temp += vb[j].first;
}
if(in3) temp += vb[3].first;
if(temp < ans) {
aid = i;
isin = in3;
ans = temp;
}
}
cout << ans << endl;
cout << s[aid] << endl;
forn(i, 3)
{
if(aid != vb[i].second)
cout << s[vb[i].second] << endl;
}
if(isin)
cout << s[vb[3].second] << endl;
return 0;
}
Problem D Distinctive Character 03:52 (+) Solved by xk (bfs)
类似于bfs的想法,每个状态走一步可以到达的状态是二进制与其有1位不同的状态。最后一个出队的点就是答案。所有状态空间是$2^{20} ≈ 10^{6}$。
我和lh盯了好久没有思路,几乎要弃题了。结果xk一拍脑门就来了一发bfs就过了。
代码:$O(2^{k})$
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#include <bitset>
#ifdef ONLINE_JUDGE
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#else
#define fast
#endif
#define forn(i, n) for(int i = 0; i < (n); i++)
#define forab(i, a, b) for(int i = (a); i <= (b); i++)
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
const int maxk = (1 << 20) + 5;
const int maxn = 1e5 + 5;
int dep[maxk];
int a[maxn];
int main()
{
fast;
int n, k;
cin >> n >> k;
forn(i, n)
{
char s[30];
cin >> s;
forn(j, k)
{
a[i] = a[i] * 2 + s[j] - '0';
}
}
sort(a, a + n);
n = unique(a, a + n) - a;
queue<int> q;
memset(dep, -1, sizeof(dep));
forn(i, n)
{
q.push(a[i]);
dep[a[i]] = 0;
}
int ans = a[0];
while(!q.empty())
{
int x = q.front(); q.pop();
forn(i, k)
{
int y = x ^ (1 << i);
// cout << bitset<5>(x) << ' ' << bitset<5>(y) << ' ' << dep[y] << endl;
if(dep[y] == -1)
{
dep[y] = dep[x] + 1;
if(dep[y] > dep[ans])
{
ans = y;
}
q.push(y);
}
// cout << bitset<5>(x) << ' ' << bitset<5>(y) << ' ' << dep[y] << endl;
}
}
for(int i = k - 1; i >= 0; i--)
{
cout << (bool)(ans & (1 << i));
}
return 0;
}
Problem E Emptying the Baltic 02:25(+) Solved by Dancepted (优先队列 + bfs)
每次挑海拔最低的点进行bfs,每个点的海拔更新为max(原来的海拔,bfs过来的点的海拔)。因为每次选的是最低的点进行bfs,所以能保证每个点第一次被更新时一定是最优的。
看了半个多小时才看出来做法,太菜了qwq。
代码:$O(hw)$
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 505
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
template <typename T, typename... Args>
inline void read(T &x, Args &... args) { read(x), read(args...); }
template <typename T>
inline void write(T x) {
int len = 0; char c[21]; if (x < 0) putchar('-'), x = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
const int dx[8] = {1, 1, 1, 0, -1, -1, -1, 0};
const int dy[8] = {1, 0, -1, -1, -1, 0, 1, 1};
struct Node{
int x, y;
ll dep;
bool operator < (const Node& x) const {
return dep > x.dep;
}
};
ll pool[N][N];
bool vis[N][N];
priority_queue <Node> Q;
int main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
read(pool[i][j]);
}
Node st;
read(st.x, st.y);
st.dep = pool[st.x][st.y];
Q.push(st);
while (!Q.empty()) {
Node tmp = Q.top(); Q.pop();
if (vis[tmp.x][tmp.y])
continue;
vis[tmp.x][tmp.y] = true;
pool[tmp.x][tmp.y] = tmp.dep;
for (int i = 0; i < 8; i++) {
Node nxt = Node{tmp.x + dx[i], tmp.y + dy[i]};
if (nxt.x <= 0 || nxt.x > n || nxt.y <= 0 || nxt.y > m)
continue;
if (vis[nxt.x][nxt.y])
continue;
nxt.dep = max(pool[nxt.x][nxt.y], tmp.dep);
Q.push(nxt);
}
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (pool[i][j] < 0)
ans -= pool[i][j];
}
}
cout << ans << endl;
return 0;
}
Problem G Galactic Collegiate Programming Contest 02:16(+) Solved by xk (模拟)
比赛中的大致思路是开M个set,对应每个解题数的队伍。如果如果其他队过题,观察题数/罚时是否超过了1号队。如果是1号队过题,算上过题数和罚时之后,在对应的题数里面暴力找1号队的排名。
复杂度的期望是O(nlogn)的,常数比较大,不过关系不大。
代码:$O(nlogn)$
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#ifdef ONLINE_JUDGE
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#else
#define fast
#endif
#define forn(i, n) for(int i = 0; i < (n); i++)
#define forab(i, a, b) for(int i = (a); i <= (b); i++)
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 1e5 + 5;
struct node
{
int solve, time;
bool operator < (const node & a) const
{
if(solve != a.solve) return solve < a.solve;
return time > a.time;
}
};
int n, m;
set<pair<int, int> > s[maxn];
int sum[maxn];
int solve[maxn], penal[maxn];
int main()
{
fast;
cin >> n >> m;
int ans = 1;
forab(i, 1, n)
{
s[0].insert(mp(0, i));
sum[i] = n;
}
for (int i = 1; i <= m; i++)
{
int t, p;
cin >> t >> p;
s[solve[t]].erase(mp(penal[t], t));
sum[solve[t]]--;
solve[t]++; penal[t] += p;
s[solve[t]].insert(mp(penal[t], t));
if(t == 1)
{
auto it = s[solve[t]].begin();
ans = n - sum[solve[t]] + 1;
while(it->first < penal[t])
{
ans++;
it++;
}
}
else
{
node t1 = node{solve[1], penal[1]};
node before = node{solve[t] - 1, penal[t] - p};
node now = node{solve[t], penal[t]};
if(!(t1 < before) && t1 < now)
{
ans++;
}
}
cout << ans << endl;
}
return 0;
}
/*
3 4
2 7
3 5
1 6
1 9
*/
赛后观摩了一下claris的博客,有个更好的模拟。
维护一个set表示比1号队排名严格靠前的队伍。其他队过题就考虑是否加入set,1队过题就考虑在set中删除被1队超过的队伍。
代码:$O(nlogn)$
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <stdio.h>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 100005
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
template <typename T, typename... Args>
inline void read(T &x, Args &... args) { read(x), read(args...); }
template <typename T>
inline void write(T x) {
int len = 0; char c[21]; if (x < 0) putchar('-'), x = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
struct Node{
int id, cnt, penalty;
bool operator < (const Node& x) const {
if(cnt == x.cnt) {
if (penalty == x.penalty)
return id < x.id;
return penalty < x.penalty;
}
return cnt > x.cnt;
}
};
set<Node> S;
set<Node> :: iterator it, jt;
int cnt[N], penalty[N];
int main() {
int n, m; cin >> n >> m;
Node concern = Node{1, 0, 0};
for (int i = 1; i <= m; i++) {
int t, p; read(t, p);
if (t == 1) {
concern.cnt++;
concern.penalty += p;
it = S.lower_bound(concern);
while (it != S.end()) {
jt = it++;
S.erase(jt);
}
}
else {
Node pre = Node{t, cnt[t], penalty[t]};
cnt[t]++, penalty[t] += p;
Node nxt = Node{t, cnt[t], penalty[t]};
if (pre < concern)
S.erase(pre);
if (nxt < concern)
S.insert(nxt);
}
printf("%d\n", sz(S) + 1);
}
return 0;
}
Problem I Import Spaghetti 01:31(+) Solved by lh (floyd)
实际上就是找出一个有向图的最小环。$dis_{i,i}$初始化为INF,跑一下floyd之后,找$dis_{i,i}$的最小值。
代码:$O(n^{3})$
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 100005
#define M 100005
#define INF 0x3f3f3f3f
using namespace std;
int n, dis[505][505], k, road[505][505];
map<string, int> id;
map<int, string> in;
string s;
int final[N], tot = 0;
void solve(int l, int r)
{
if(dis[l][r] == 1)
return;
solve(l, road[l][r]), final[++tot] = road[l][r], solve(road[l][r], r);
}
int ac()
{
fast;
cin >> n;
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= n; ++i)
cin >> s, id[s] = i, in[i] = s;
for(int i = 1; i <= n; ++i)
{
cin >> s >> k;
char c;
for (int j = 1; j <= k; ++j)
{
cin >> s >> s;
while (s[s.size() - 1] == ',')
{
s.pop_back();
dis[i][id[s]] = 1;
cin >> s;
}
dis[i][id[s]] = 1;
}
}
for (int i = 1;i <= n; ++i)
for (int j = 1;j <= n; ++j)
for (int k = 1;k <= n; ++k)
{
if (dis[j][k] > dis[j][i] + dis[i][k])
dis[j][k] = dis[j][i] + dis[i][k], road[j][k] = i;
}
int ans = 1;
bool flag = false;
for (int i = 1;i <= n; ++i)
{
if (dis[i][i] != INF)
{
flag = true;
if (dis[ans][ans] > dis[i][i])
ans = i;
}
}
if (!flag)
{
puts("SHIP IT");
return 0;
}
solve(ans, ans), cout << in[ans];
for (int i = 1; i <= tot; ++i)
cout << " " << in[final[i]];
}
int main()
{
ac();
return 0;
}
/*
4
a b c d
a 1
import d, b, c
b 2
import d
import c
c 1
import c
d 0
*/
Problem J Judging Moose 00:12 (+) Solved by Dancepted
printf级别的签到。12分钟才过是因为电脑网速太慢打不开pdf。
代码:
Problem K Kayaking Trip 04:30 (-2) Solved by Dancepted (二分答案+贪心)
答案单调是显然的。贪心就是对每艘船选择最小的选手能力值的组合,使得这个组合与船的系数的乘积,能不小于答案。
读完题我就猜是这么写的,但是正确性有点不确定。然后lh想了个假的暴力交上去wa了两发后,被我hack了。
代码(附一个样例):$O((b+n+e)log(2 * c_{max} * s_{max})$
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 100005
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
template <typename T, typename... Args>
inline void read(T &x, Args &... args) { read(x), read(args...); }
template <typename T>
inline void write(T x) {
int len = 0; char c[21]; if (x < 0) putchar('-'), x = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
int sum = 0;
int cnt[3], val[3], boat[N];
int ccnt[3];
bool check(int mid) {
for (int i = 0; i < 3; i++)
ccnt[i] = cnt[i];
for (int i = 0; i < sum/2; i++) {
int pa = 2, pb = 2;
for (int a = 0; a < 3; a++) if (ccnt[a]) {
ccnt[a]--;
for (int b = a; b < 3; b++) if (ccnt[b]) {
ccnt[b]--;
if (mid <= boat[i] * (val[a] + val[b]) && val[a] + val[b] < val[pa] + val[pb]) {
pa = a;
pb = b;
}
ccnt[b]++;
}
ccnt[a]++;
}
if (mid > boat[i] * (val[pa] + val[pb]))
return false;
if (pa != pb) {
if (ccnt[pa] && ccnt[pb]) {
ccnt[pa]--, ccnt[pb]--;
}
else
return false;
}
else if (pa == pb) {
if (ccnt[pa] >= 2) {
ccnt[pa] -= 2;
}
else
return false;
}
}
return true;
}
int main() {
for (int i = 0; i < 3; i++)
read(cnt[i]),
sum += cnt[i];
for (int i = 0; i < 3; i++)
read(val[i]);
for (int i = 0; i < sum/2; i++) {
read(boat[i]);
}
sort(boat, boat + sum/2);
int l = 0, r = INF, ans = 0;
// int l = 0, r = 5000, ans = 0;
while (l <= r) {
int mid = (l+r) >> 1;
if (check(mid)) {
ans = max(ans, mid);
l = mid+1;
}
else {
r = mid-1;
}
}
cout << ans << endl;
return 0;
}
/*
4 4 4
1 2 5
42 60 70 105 140 210
*/
总结:
封榜的时候已经没事干了,我抓起键盘想着莽一发K的二分+贪心,居然真的就过了。模拟赛拿金成就get√。
第一次模拟赛金着实开心,但是想了想这场打得也不是很舒服,也没有发挥得很好。前期开题各种不顺,但是看排名就是掉不下去。可能是这个赛区的选手弱了吧。
上海站还有10天不到的时间,理论上是能冲到银首的位置的。就看能不能开出一道金牌题了。
接下来几天看看进阶指南,学点没学过的算法,打完ccpcf回来再练几场模拟赛,应该就差不多了。
2019冲鸭!