Dancepted

Dancing Acceped!

模拟赛小结: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;
}
View Code

 

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;
}
View Code

 

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;
}
View Code

 

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
*/
View Code

赛后观摩了一下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;
}
View Code

 

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
*/
View Code

 

Problem J Judging Moose 00:12 (+) Solved by Dancepted

printf级别的签到。12分钟才过是因为电脑网速太慢打不开pdf。

代码:

View Code

 

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
*/
View Code

 

总结

封榜的时候已经没事干了,我抓起键盘想着莽一发K的二分+贪心,居然真的就过了。模拟赛拿金成就get√。

第一次模拟赛金着实开心,但是想了想这场打得也不是很舒服,也没有发挥得很好。前期开题各种不顺,但是看排名就是掉不下去。可能是这个赛区的选手弱了吧。

上海站还有10天不到的时间,理论上是能冲到银首的位置的。就看能不能开出一道金牌题了。

接下来几天看看进阶指南,学点没学过的算法,打完ccpcf回来再练几场模拟赛,应该就差不多了。

2019冲鸭!

 

posted on 2019-11-13 13:01  Danceped  阅读(323)  评论(3编辑  收藏  举报

导航