The 2018 ACM-ICPC Asia Beijing Regional Contest

Posted dup4

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了The 2018 ACM-ICPC Asia Beijing Regional Contest相关的知识,希望对你有一定的参考价值。

Contest Info


Practice Link

Solved A B C D E F G H I J
6/10 O O - O - ? - O O -
  • O 在比赛中通过
  • ? 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A - Jin Yong’s Wukong Ranking List

题意:
给出(n)对有向关系,判断前多少对关系会形成一个环。

思路:
慢慢加入一对关系,跑拓扑排序,出现环就停止。

代码:


view code

#include <bits/stdc++.h>
using namespace std;

const int N = 510;
int n;
map<string, int> mp; int tot;
string s[2][N];
int getid(string s) {
    if (mp.count(s)) return mp[s];
    mp[s] = ++tot;
    return mp[s];
}

vector <vector<int>> G;
int d[N];
bool gao(int n) {
    memset(d, 0, sizeof d);
    G.clear(); G.resize(tot + 1);
    for (int i = 1; i <= n; ++i) {
        int u = getid(s[0][i]), v = getid(s[1][i]);
        ++d[v];
        G[u].push_back(v);
    }
    int cnt = 0;
    queue <int> que; 
    for (int i = 1; i <= tot; ++i) if (!d[i]) que.push(i);
    while (!que.empty()) {
        int u = que.front(); que.pop();
        ++cnt;
        for (auto &v : G[u]) {
            if (--d[v] == 0) {
                que.push(v);
            }
        }
    }
    return cnt != tot;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    while (cin >> n) {
        mp.clear(); tot = 0;
        for (int i = 1; i <= n; ++i) {
            cin >> s[0][i] >> s[1][i];
            getid(s[0][i]); getid(s[1][i]);
        }
        bool flag = 0;
        for (int i = 1; i <= n; ++i) {
            if (gao(i)) {
                cout << s[0][i] << " " << s[1][i] << "
";
                flag = 1;
                break;
            }
        }
        if (!flag) cout << 0 << "
";
    }
    return 0;
}

B - Heshen‘s Account Book

题意:
模拟题。给出若干行文本,包含空格、数字、字母。
找出其中所有连续的自然数,但是如果某一行的结尾是数字,并且下一行的开头也是数字,那么这两行的数字视为连在一起的。

思路:
直接将所有行连在一起再判断。
能直接连就直接连,不能直接连中间加一个空格。
不要分类讨论做,很多Case考虑不到。

代码:


view code

#include <bits/stdc++.h>
using namespace std;
using pSI = pair<string, int>;
#define fi first
#define se second
const int N = 2e5 + 10;
int vis[N], num[N], pos, len;
string s, t;
bool isnum(string &s) {
    int len = s.size();
    if (len == 1) {
        return isdigit(s[0]);
    } 
    if (s[0] == '0') return false;
    for (int i = 0; i < len; ++i)
        if (!isdigit(s[i]))
            return false;
    return true;
}

pSI get() {
    pSI tmp = pSI("", -1);
    while (pos < len && t[pos] == ' ') ++pos;
    while (pos < len && t[pos] != ' ') {
        if (tmp.se == -1) tmp.se = vis[pos];
        tmp.fi += t[pos];
        ++pos; 
    }
    return tmp;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    memset(vis, 0, sizeof vis);
    memset(num, 0, sizeof num);
    s = t = "";
    int pre = -1; t = "";
    int n = 0;
    while (getline(cin, s)) {
        ++n;
        if (!t.empty() && isdigit(t.end()[-1]) && isdigit(s[0])) {
            t += s;
        } else {
            t += " ";
            t += s;
            ++pre;
        }
        int len = t.size();
        for (int i = pre + 1; i < len; ++i) vis[i] = n;
        pre = len - 1;  
    } 
    len = t.size();
    pos = 0;
    vector <string> vec;
    while (1) {
        pSI tmp = get();
        if (tmp.se == -1) break;
        if (isnum(tmp.fi)) {
            ++num[tmp.se];
            vec.push_back(tmp.fi);
        }
    }
    int sze = vec.size();
    for (int i = 0; i < sze; ++i)
        cout << vec[i] << " 
"[i == sze - 1];
    for (int i = 1; i <= n; ++i)
        cout << num[i] << "
";
    return 0;
}

C - Pythagorean triple

题意:
统计有多少个三元组((a, b, c))使得(a^2 + b^2 = c^2),并且满足(c leq N)

D - Frog and Portal

题意:
现在有(201)个点,可以加一些传送门,一旦进入这个点就会被传送到另一个点。
现在青蛙在(0)号点,它要到(200)号点,它如果在(p)号点,那么下一步可以去(p + 1)或者(p + 2)号点。
问你如何加传送门,使得它从(0)(200)的方案数是(m)

思路:
考虑不加任何传送门方案数是第(201)个斐波那契数。
我们可以考虑任意一个整数都可以被分解为若干个斐波那契数相加。
那么我们从起点的某个地方直接传送到(201 - x)那个点,那么从(x)(201)的方案数就是第(x)个斐波那契数。
并且控制从(0)走到那个传送门的方案为(1)即可。

代码:


view code

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 110;

ll m;
ll f[N];
int a[N];

int main() {
    f[0] = 1, f[1] = 1;
    for (int i = 2; i <= 50; ++i) {
        f[i] = f[i - 1] + f[i - 2];
    }
    while (scanf("%lld", &m) != EOF) {
        if (m == 0) {
            puts("2
1 1
2 1");
            continue;
        }
        *a = 0;
        for (int i = 50; i >= 1; --i) {
            if (m >= f[i]) {
                a[++*a] = i;
                m -= f[i];
            }
        }
        printf("%d
", *a + 1);
        for (int i = 1; i <= *a; ++i) {
            printf("%d %d
", 2 * i - 1, 200 - a[i]);
        }
        printf("%d %d
", 2 * (*a), 2 * (*a));
    }
    return 0;
}

F - The Kth Largest Value

题意:

思路:

代码:


view code

#include <bits/stdc++.h>
using namespace std;
#define dbg(x...) do { cout << "33[32;1m" << #x << " -> "; err(x); } while (0) 
void err() { cout << "33[39;0m" << endl; } 
template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }
using ll = long long;
using ull = unsigned long long;
const int N = 5e4 + 10, M = 2e5 + 10;
struct Bitset {
    #define W (64)

    int n;
    ull bits[N / W + 10];
    int num[N / W + 10];

    void preWork() {
        for(int i = 0;i <= n / W; ++i) num[i] = __builtin_popcountll(bits[i]);
        for(int i = n / W - 1;i >= 0; --i) num[i] += num[i + 1];
     //  for(int i = 0;i < m;i ++) printf("%d ",sum[i]); puts("");
      //  for(int i = 0;i <= n/W;i ++) printf("%llu ",bits[i]); puts("");
    }

    int ask(int x) {
        if(x > n) return 0; 
        int blockid = x / W;
        int ans = __builtin_popcountll(bits[blockid]>>(x%W));
        blockid++;
        if(blockid <= n/W) ans += num[blockid];
        return ans;
    }

    int ask(int l,int r) {
        if (l > r) return 0;
        return ask(l) - ask(r+1);
    }

    void Xor(const Bitset &t) {
        for (int i = 0; i <= n / W; ++i) bits[i] ^= t.bits[i]; 
    }

    void And(const Bitset &t) {
        for (int i = 0; i <= n / W; ++i) bits[i] &= t.bits[i];
    }

    void Or(const Bitset &t) {
        for(int i = 0; i <= n / W; ++i) bits[i] |= t.bits[i];
    }

    void Copy(const Bitset &t) {
        n = t.n;
        for(int i = 0; i <= n / W; ++i) bits[i] = t.bits[i];
    }

    void Set(int x) { 
        bits[x / W] |= 1llu << (x % W);
    }

    void Reset(int x) {
        Set(x);
        bits[x / W] ^= 1llu << (x % W);
    }

    void init(int _n) {
        n = _n + 1; //n++;
        for(int i = 0; i <= n / W; i++) bits[i] = 0;
    }

    void print() {
        for(int i = 0; i <= n; ++i) { 
            if(bits[i / W] >> (i % W) & 1) 
                printf("%d ", i);
        }
        puts("");
    }

    #undef W
}bs[N], bg[N];
int n, m, q, f[N];
vector <vector<int>> G; 
struct Tarjan {
    int Low[N], DFN[N], sta[N], Belong[N], num[N], d[N], scc; 
    bool Insta[N];
    void dfs(int u) {
        Low[u] = DFN[u] = ++*Low;
        sta[++*sta] = u;
        Insta[u] = 1;
        for (auto &v : G[u]) {
            if (!DFN[v]) {
                dfs(v);
                Low[u] = min(Low[u], Low[v]);
            } else if (Insta[v]) {
                Low[u] = min(Low[u], DFN[v]);
            }
        }
        if (Low[u] == DFN[u]) {
            ++scc;
            int v;
            do {
                v = sta[(*sta)--];
                Insta[v] = 0;
                Belong[v] = scc;
                ++num[scc];
            } while (v != u);
        }
    }
    void gao() {
        memset(DFN, 0, sizeof DFN);
        memset(Insta, 0, sizeof Insta);
        memset(num, 0, sizeof num);
        memset(d, 0, sizeof d);
        scc = *sta = *Low = 0;
        for (int i = 1; i <= n; ++i) if (!DFN[i]) dfs(i);   
        vector <vector<int>> H(scc + 1), bk(scc + 1);
        for (int i = 1; i <= scc; ++i) bg[i].init(n); 
        for (int u = 1; u <= n; ++u) {
            bk[Belong[u]].push_back(u);
            for (auto &v : G[u]) {
                if (Belong[u] == Belong[v]) {
                    continue;
                }
                H[Belong[v]].push_back(Belong[u]);
                ++d[Belong[u]];
            }
        }
        queue <int> que;
        for (int i = 1; i <= scc; ++i) if (!d[i]) que.push(i);
        while (!que.empty()) {
            int u = que.front(); que.pop();
            for (auto &it : bk[u]) {
                bg[u].Set(it); 
            }
            for (auto &v : H[u]) {
                bg[v].Or(bg[u]); 
                if (--d[v] == 0) {
                    que.push(v);
                }
            }
        }
        for (int i = 1; i <= n; ++i) {
            bs[i] = bg[Belong[i]]; 
            bs[i].Set(i);
            bs[i].preWork();
        }
    }
}tarjan;

int main() {
    int _T; scanf("%d", &_T);
    while (_T--) {
        scanf("%d%d%d", &n, &m, &q);
        G.clear(); G.resize(n + 1); 
        for (int i = 1, u, v; i <= m; ++i) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
        }
        tarjan.gao();
    //  for (int i = 1; i <= n; ++i) bs[i].print();
        int len = 1, cnt = 1;
        while (len < n * 2) len <<= 1, ++cnt;  
        while (q--) {
            ll K; int res = 0; scanf("%lld", &K); --K; 
            memset(f, 0, sizeof f); 
            for (int i = cnt; i >= 0; --i) {
                //试着放1 
                ll num = 0;
                int bit = 1 << i; 
                for (int j = 1; j <= n; ++j) {
                    if (((j >> i) & 1) == 0) { 
                        f[j] |= bit; 
                    }
                    num += bs[j].ask(f[j], min(n, (f[j] | (bit - 1)))); 
                    if (((j >> i) & 1) == 0) {
                        f[j] ^= bit;
                    }
                }
                if (K >= num) {
                    K -= num; 
                    for (int j = 1; j <= n; ++j) {
                        if ((j >> i) & 1) {
                            f[j] |= bit;
                        }
                    }
                } else {
                    res |= bit; 
                    for (int j = 1; j <= n; ++j) {
                        if (((j >> i) & 1) == 0) {
                            f[j] |= bit;
                        }
                    }
                }
            }
            printf("%d
", res);
        }
    }
    return 0;
}

H - Approximate Matching

题意:
给出一个长度为(n)的模式串,询问你有多少个长度为(m)的文本串,使得模式串可以在文本串中被匹配上。
匹配过程中可以有一位是失配的。

思路:
考虑将模式串拆成(n)个不同的模式串,那么就转化成了完全匹配。
(n)个模式串插入(AC)自动机,然后考虑(f[i][j])表示到了文本串的第(i)位,并且匹配指针到了(AC)自动机上的第(j)个结点的方案数。
一旦匹配上了,那么后面的字符任意放直接算贡献,并且这个方案不需要转移给下一位。
也就是说我们把贡献算在第一次匹配的位置。

或者可以将所有没有匹配上的结点进行(dp),这样最后算出来的是不合法的方案数,拿总方案减去即可。

代码:


view code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e4 + 10, ALP = 2;
int n, m; char s[N];
ll f[50][N];
struct ACAM {
    struct node {
        int nx[ALP], fail;
        int cnt; 
        node() {
            memset(nx, -1, sizeof nx);
            cnt = 0; 
        }
    }t[N];
    int root, tot;
    int que[N], ql, qr;
    int newnode() {
        ++tot;
        t[tot] = node();
        return tot;
    }
    void init() {
        tot = 0;
        root = newnode();
    }
    void insert(char *s) {
        int len = strlen(s);
        int now = root;
        for (int i = 0; i < len; ++i) {
            if (t[now].nx[s[i] - '0'] == -1) 
                t[now].nx[s[i] - '0'] = newnode();
            now = t[now].nx[s[i] - '0'];
        }
        ++t[now].cnt;
    }
    void build() {
        ql = 1, qr = 0;
        t[root].fail = root;
        for (int i = 0; i < ALP; ++i) {
            if (t[root].nx[i] == -1) {
                t[root].nx[i] = root;
            } else {
                t[t[root].nx[i]].fail = root;
                que[++qr] = t[root].nx[i];
            }
        }
        while (ql <= qr) {
            int now = que[ql++];
            for (int i = 0; i < ALP; ++i) {
                if (t[now].nx[i] == -1) {
                    t[now].nx[i] = t[t[now].fail].nx[i];
                } else {
                    t[t[now].nx[i]].fail = t[t[now].fail].nx[i];
                    que[++qr] = t[now].nx[i];
                }
            }
        }
    }
    ll gao() {
        ll res = 0;
        for (int i = 0; i <= m; ++i)
            for (int j = 0; j <= tot; ++j)
                f[i][j] = 0;
        f[0][root] = 1;
        for (int i = 0; i < m; ++i) {
            for (int j = 1; j <= tot; ++j) {
                if (t[j].cnt > 0) continue;
                for (int k = 0; k < 2; ++k) {
                    if (t[j].nx[k] != -1) { 
                        f[i + 1][t[j].nx[k]] += f[i][j];
                        continue;
                    } 
                }
            }
        }
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= tot; ++j) {
                if (t[j].cnt > 0) {
                    res += f[i][j] * (1ll << (m - i));
                }
            }
        }

        return res;
    }
}acam;

int main() {
    int _T; scanf("%d", &_T);
    while (_T--) {
        scanf("%d%d%s", &n, &m, s);
        acam.init(); acam.insert(s);
        for (int i = 0; i < n; ++i) {
            s[i] = ((s[i] - '0') ^ 1) + '0';
            acam.insert(s);
            s[i] = ((s[i] - '0') ^ 1) + '0';
        }
        acam.build();
        printf("%lld
", acam.gao());
    }
    return 0;
}

I - Palindromes

题意:
找第(k)个回文数。(k)很大。

思路:
找规律。

代码:


view code

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;
char s[N];

int main() {
    int _T; scanf("%d", &_T);
    while (_T--) {
        scanf("%s", s + 1);
        int len = strlen(s + 1);
        if (len == 1) {
            printf("%c
", s[1] - 1);
            continue;
        } 
        if (s[1] > '1') {
            s[1]--;
            printf("%s", s + 1);
            reverse(s + 1, s + len);
            s[len] = 0;
            printf("%s", s + 1);
        } else if (s[2] == '0') {
            for (int i = 1; i < len; ++i)
                s[i] = s[i + 1];
            s[1] = '9';
            s[len] = 0; len--;
            printf("%s", s + 1);
            reverse(s + 1, s + len);
            s[len] = 0;
            printf("%s", s + 1);
        } else {
            for (int i = 1; i < len; ++i)
                s[i] = s[i + 1];
            s[len] = 0; len--;
            printf("%s", s + 1);
            reverse(s + 1, s + len + 1);
            printf("%s", s + 1);
        }
        puts("");
    }
    return 0;
}

J - Rikka with Triangles

题意:
给出(n)个点,计算这(n)个点组成的所有锐角三角形的面积和

以上是关于The 2018 ACM-ICPC Asia Beijing Regional Contest的主要内容,如果未能解决你的问题,请参考以下文章

The 2018 ACM-ICPC Asia Qingdao Regional Contest

The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online J Press the Button

The 2018 ACM-ICPC Asia Beijing Regional Contest

The 2018 ACM-ICPC Asia Qingdao Regional Contest K XOR Clique

The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online - H Traveling on the Axis-思维模拟题目

I. Distance (模拟)2018-2019 ACM-ICPC, Asia Jiaozuo Regional Contest