2019 ICPC Asia Yinchuan Regional

Contest Info


Practice Link

Solved A B C D E F G H I J K L M N
9/14 O O - O - O O O O - O - - O
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Girls Band Party

B. So Easy

题意:
给出一个\(n \cdot n\)的矩形,这个矩形\(a_{i, j}\)的初始值为\(0\),它每次能够选择一行或者一列加上\(1\),现在遮住某个位置的数,让你还原这个数。

思路:
考虑倒退操作,不考虑遮住的那个数,然后枚举每行,每列,每次选择行最小,列最小将整行整列减去即可还原出那个数。

代码:


view code

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int n, a[N][N];

int main() {
    while (scanf("%d", &n) != EOF) {
        int x = -1, y = -1;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                scanf("%d", &a[i][j]);
                if (a[i][j] == -1) {
                    x = i, y = j;
                    a[i][j] = 0;
                }
            }
        }
        for (int i = 1; i <= n; ++i) {
            int Min = 1e9;
            for (int j = 1; j <= n; ++j) {
                if (x == i && y == j) continue;
                Min = min(Min, a[i][j]);
            }
            for (int j = 1; j <= n; ++j) {
                a[i][j] -= Min;
            }
        }
        for (int j = 1; j <= n; ++j) {
            int Min = 1e9;
            for (int i = 1; i <= n; ++i) {
                if (x == i && y == j) continue;
                Min = min(Min, a[i][j]);
            }
            for (int i = 1; i <= n; ++i) {
                a[i][j] -= Min;
            }
        }
        printf("%d\n", -a[x][y]);
    }
    return 0;
}

D. Easy Problem

题意:
定义一个序列\((a_1, a_2, \cdots, a_n)\)是一个\((n, m, d)-good\)当且仅当\(1 \leq a_i \leq m(1 \leq i \leq n)\)并且\(gcd(a_1, a_2, \cdots, a_n) = d\)
\(f(a, k) = (a_1a_2\cdots a_n)^k\),现在给出\(n, m, d, k\),让你求所有合法的\((n, m, d)-good\)的序列\(a\)\(f(a, k)\)

思路:
题目要求的东西等价于:
\[ \begin{eqnarray*} f(d) = \sum\limits_{a_1 = 1}^m \sum\limits_{a_2 = 1}^m \cdots \sum\limits_{a_n = 1}^m [gcd(a_1, a_2, \cdots, a_n) = d](a_1a_2\cdots a_n)^k \end{eqnarray*} \]
那么我们令:
\[ \begin{eqnarray*} g(d) = \sum\limits_{a_1 = 1}^m \sum\limits_{a_2 = 1}^m \cdots \sum\limits_{a_n = 1}^m [d | gcd(a_1, a_2, \cdots, a_n)](a_1a_2\cdots a_n)^k \end{eqnarray*} \]
显然有:
\[ \begin{eqnarray*} g(d) = (\sum\limits_{d\;|\;i} i^k)^n \end{eqnarray*} \]
莫比乌斯反演有:
\[ \begin{eqnarray*} f(d) &=& \sum\limits_{d\;|\;i} \mu(\frac{i}{d})g(i) \\ &=& \sum\limits_{d\;|\;i} \mu(\frac{i}{d}) (\sum\limits_{i\;|\;j} j^k)^n \end{eqnarray*} \]
所以:
\[ f(d) = \sum\limits_{i = 1}^{\left\lfloor m/d \right\rfloor} \mu(i) (\sum\limits_{id\;|\;j} j^k)^n \]

代码:


view code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 10, mod = 59964251;
int pri[N], check[N], mu[N], n, m, d, K, phi; 
char s[N]; 
void sieve() {
    memset(check, 0, sizeof check);
    *pri = 0;
    mu[1] = 1;
    for (int i = 2; i < N; ++i) {
        if (check[i] == 0) {
            pri[++*pri] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= *pri; ++j) {
            if (i * pri[j] >= N) break;
            check[i * pri[j]] = 1;
            if (i % pri[j] == 0) {
                mu[i * pri[j]] = 0;
                break;
            } else {
                mu[i * pri[j]] = -mu[i];
            }
        }
    }
}

int eular(int n) {
    int ans = n;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            ans -= ans / i;
            while (n % i == 0) 
                n /= i;
        }
    }
    if (n > 1) ans -= ans / n;
    return ans;
}

ll gcd(ll a, ll b) {
    return b ? gcd(b, a % b) : a;
}

ll qmod(ll base, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) res = res * base % mod;
        base = base * base % mod;
        n >>= 1;
    }
    return res;
}

int getMod(int mod) {
    int res = 0;
    for (int i = 1; s[i]; ++i) {
        res = (res * 10 + s[i] - '0') % mod;
    }
    return res;
}

int main() {
    phi = eular(mod);
    sieve();    
//  cout << phi << endl;
    int _T; scanf("%d", &_T);
    while (_T--) {
        scanf("%s%d%d%d", s + 1, &m, &d, &K);
        int len = strlen(s + 1);
        if (len <= 9) {
            n = 0;
            for (int i = 1; s[i]; ++i) {
                n = n * 10 + s[i] - '0';
            }
        } else {
            n = getMod(phi);
            if (getMod(643) == 0 || getMod(93257) == 0) {
                n += phi;
            }
        }
        ll res = 0;
        for (int i = 1; i <= m / d; ++i) {
            int base = 0;
            for (int j = i * d; j <= m; j += i * d) {
                base += qmod(j, K);
                base %= mod;
            }
            res += 1ll * mu[i] * qmod(base, n) % mod;
            res = (res + mod) % mod;
        }
        printf("%lld\n", res);
    }
    return 0;
}

E. XOR Tree

题意:
定义一个multiset的权值为里面任意两个数的异或和的平方的和。
现在给出一棵有根树(\(1\)为根),每个点有点权,定义\(p(x, k)\)\(x\)子树中距离\(x\)不超过\(k\)的所有点的点权构成的multiset的权值,现在要对每个\(i \in [1, n]\)\(p(i, k)\)

F. Function!

G. Pot!!

裸的线段树。

代码:


view code

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, q;
struct SEG {
    struct node {
        int Max, lazy;
        node() { Max = lazy = 0; }
        void up(int x) {
            Max += x;
            lazy += x;
        }
        node operator + (const node &other) const {
            node res = node();
            res.Max = max(Max, other.Max);
            return res;
        }
    }t[N << 2];
    void build(int id, int l, int r) {
        t[id] = node();
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
    }
    void down(int id) {
        int &lazy = t[id].lazy;
        if (lazy) {
            t[id << 1].up(lazy);
            t[id << 1 | 1].up(lazy);
            lazy = 0;
        }
    }
    void update(int id, int l, int r, int ql, int qr, int v) {
        if (l >= ql && r <= qr) {
            t[id].up(v);
            return;
        }
        int mid = (l + r) >> 1;
        down(id);
        if (ql <= mid) update(id << 1, l, mid, ql, qr, v);
        if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, v);
        t[id] = t[id << 1] + t[id << 1 | 1];
    }
    int query(int id, int l, int r, int ql, int qr) {
        if (l >= ql && r <= qr) return t[id].Max;
        int mid = (l + r) >> 1;
        down(id);
        int res = 0;
        if (ql <= mid) res = max(res, query(id << 1, l, mid, ql, qr));
        if (qr > mid) res = max(res, query(id << 1 | 1, mid + 1, r, ql, qr));
        return res;
    }
}seg[4];

int main() {
    int id[] = {0, 0, 0, 1, 0, 2, 0, 3, 0};
    vector <vector<int>> vec;
    vec.resize(15);
    for (int i = 2; i <= 10; ++i) {
        int x = i;
        vec[i].clear();
        for (int j = 2; j <= x; ++j) {
            while (x % j == 0) {
                vec[i].push_back(j);
                x /= j;
            }
        }
    //  cout << i << endl;
    //  for (auto &it : vec[i])
    //      cout << it << " ";
    //  cout << endl;
    }
    while (scanf("%d%d", &n, &q) != EOF) {
        for (int i = 0; i < 4; ++i) seg[i].build(1, 1, n);
        char op[20]; int l, r, x;
        while (q--) {
            scanf("%s%d%d", op, &l, &r);
            if (op[1] == 'U') {
                scanf("%d", &x);
                for (auto &it : vec[x]) {
                    seg[id[it]].update(1, 1, n, l, r, 1);
                }
            } else {
                int res = 0;
                for (int i = 0; i < 4; ++i) {
                    res = max(res, seg[i].query(1, 1, n, l, r));
                }
                printf("ANSWER %d\n", res);
            }
        }   
    }
    return 0;
}

H. Delivery Route

题意:
给出一张图,有\(x\)条无向边,有\(y\)条有向边,保证无向边都是正权值,有向边可能有负权值,并且保证如果一条有向边\(a_i \rightarrow b_i\),那么在该图中,\(b_i\)不可能到达\(a_i\)
现在询问从\(s\)出发到任意一点的最短路。

思路:
我们考虑如果只考虑有向边,那么是一个\(DAG\),那么把无向边连成的每个联通块看成一个新点,并且有有向边将他们连接起来,他们也是一个\(DAG\)
并且无向图的连通块里面没有负权边,可以跑dijkstra,然后根据拓扑序dp一下即可。

代码:


view code

#include <bits/stdc++.h>
using namespace std;
using pII = pair<int, int>;
#define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }
#define fi first
#define se second
const int N = 5e4 + 10, INF = 0x3f3f3f3f;
int n, mx, my, s, id[N], d[N];
//0 two-way 1 one-way
vector <vector<pII>> G[3];
vector <vector<int>> po;
struct DSU {
    int fa[N];
    void init() { memset(fa, 0, sizeof fa); }
    int find(int x) {
        return fa[x] == 0 ? x : fa[x] = find(fa[x]);
    }
    void merge(int u, int v) {
        int fu = find(u), fv = find(v);
        if (fu != fv) {
            fa[fu] = fv;
        }
    }
}dsu;

struct node {
    int u, w;
    node() {}
    node(int u, int w) : u(u), w(w) {}
    bool operator < (const node &other) const {
        return w > other.w;
    }
};
int dis[N], used[N];
void Dijkstra(int S) {
    priority_queue <node> pq;
    pq.push(node(S, dis[S]));
    while (!pq.empty()) {
        int u = pq.top().u; pq.pop();
        for (auto &it : G[0][u]) { 
            int v = it.fi, w = it.se;
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                pq.push(node(v, dis[v]));
            }   
        }
    }
}

void Topo() {
    queue <int> que;
    for (int i = 1; i <= *id; ++i) {
        if (d[i] == 0) {
            que.push(i);
        }
    }   
    while (!que.empty()) {
        int u = que.front(); que.pop();
        sort(po[u].begin(), po[u].end(), [&](int a, int b) { return dis[a] < dis[b]; });
        for (auto &it : po[u]) {
            Dijkstra(it);
            for (auto &it2 : G[1][it]) {
                int v = it2.fi, w = it2.se;
                if (dis[it] < INF) {
                    dis[v] = min(dis[v], dis[it] + w);
                }
            }
        }   
        for (auto &it : G[2][u]) {
            int v = it.fi;
            if (--d[v] == 0) {
                que.push(v);
            }
        }
    }
}

int main() {
    while (scanf("%d%d%d%d", &n, &mx, &my, &s) != EOF) {
        for (int i = 1; i <= n; ++i) {
            dis[i] = INF;
        }
        dis[s] = 0;
        *id = 0;
        G[0].clear(); G[1].clear();
        G[0].resize(n + 1); G[1].resize(n + 1);
        for (int i = 1, u, v, w; i <= mx; ++i) {
            scanf("%d%d%d", &u, &v, &w);
            G[0][u].push_back(pII(v, w));
            G[0][v].push_back(pII(u, w));
        }
        for (int i = 1, u, v, w; i <= my; ++i) {
            scanf("%d%d%d", &u, &v, &w);
            G[1][u].push_back(pII(v, w));
        }
        dsu.init();
        for (int u = 1; u <= n; ++u) {
            for (auto &it : G[0][u]) {
                int v = it.fi;
                dsu.merge(u, v);
            }
        }
        for (int u = 1; u <= n; ++u) {
            if (dsu.fa[u] == 0)
                id[u] = ++*id;
        }
        for (int u = 1; u <= n; ++u) {
            if (dsu.fa[u]) {
                id[u] = id[dsu.find(u)];
            }
        }
    //  for (int i = 1; i <= n; ++i)
    //      dbg(i, id[i]); 
        po.clear(); po.resize(*id + 10);
        G[2].clear(); G[2].resize(*id + 10);
        memset(d, 0, sizeof d);
        for (int u = 1; u <= n; ++u) {
            po[id[u]].push_back(u); 
            for (auto &it : G[1][u]) { 
                int v = it.fi;
                if (id[u] != id[v]) {
                    G[2][id[u]].push_back(pII(id[v], v));
                    ++d[id[v]];
                }
            }
        }
        Topo();
        for (int i = 1; i <= n; ++i) {
            if (dis[i] >= INF) puts("NO PATH");
            else printf("%d\n", dis[i]);
        }
        
    }
    return 0;
}

I. Base62

题意:
进制转换

代码:


view code


def main():
    l = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
         'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
         'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
    x, y, z = input().split()
    x = int(x)
    y = int(y)
    num = 0
    for c in z:
        if ord('0') <= ord(c) <= ord('9'):
            num = num * x + ord(c) - ord('0')
        elif ord('A') <= ord(c) <= ord('Z'):
            num = num * x + ord(c) - ord('A') + 10
        else:
            num = num * x + ord(c) - ord('a') + 36
    if num == 0:
        print("0")
        return 
    res = ""
    while num > 0:
        tmp = num % y
        res = res + l[tmp]
        num = num // y
    # print(res)
    res = res[::-1]
    print(res)

main()

K. Largest Common Submatrix

题意:
给出两个\(n \cdot m\)的矩形,并且里面的数是\([1, nm]\)的排列。
求两个矩形的最大公共子矩形,这里的大定义为面积。

思路:
考虑里面的数是一个排列,可以\(O(n^2)\)处理出每个数向上拓展多少,向左右拓展多少,然后考虑每一行,肯定是某段连续的长度然后乘上这段最小的向上拓展的数量。
向上拓展的这部分当成一个维,位置当成一维,丢进笛卡尔树里面跑一跑能跑出每个最小值管辖的范围,然后和左右扩展的范围求交,就是每个向上拓展数量在横向最远能扩展的范围。

代码:


view code

#include <bits/stdc++.h>
using namespace std;
using pII = pair<int, int>; 
#define dbg(x...) do { cout << "\033[32;1m" << #x << " -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }
#define fi first
#define se second
const int N = 1e3 + 10, INF = 0x3f3f3f3f;
int n, m, a[N][N], b[N][N], l[N][N], r[N][N], num[N][N];
pII c[N], id[N * N];

struct CT {
    struct node {
        int id, val, fa;
        int son[2];
        node() {}
        node (int id, int val, int fa) : id(id), val(val), fa(fa) {
            son[0] = son[1] = 0;
        }
        bool operator < (const node &other) const {
            return val < other.val;
        }
    }t[N];
    int root;
    void init() {
        t[0] = node(0, -INF, 0);
    }
    void build(int n, int *a) {
        for (int i = 1; i <= n; ++i) {
            t[i] = node(i, a[i], 0);
        }
        for (int i = 1; i <= n; ++i) {
            int k = i - 1;
            while (t[i] < t[k]) {
                k = t[k].fa;
            }
            t[i].son[0] = t[k].son[1];
            t[k].son[1] = i;
            t[i].fa = k;
            t[t[i].son[0]].fa = i;
        }
        root = t[0].son[1];
    }
    int dfs(int u) {
        if (!u) return 0;
        c[u].fi = dfs(t[u].son[0]);
        c[u].se = dfs(t[u].son[1]);
        return c[u].fi + c[u].se + 1;
    }
}ct;

int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                scanf("%d", &a[i][j]);
            }
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                scanf("%d", &b[i][j]);
                id[b[i][j]] = pII(i, j); 
            }
        }

        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                if (j == 1) {
                    l[i][j] = j;
                } else {
                    int pre = a[i][j - 1], now = a[i][j];
                    if (id[pre].fi == id[now].fi && id[pre].se == id[now].se - 1) {
                        l[i][j] = l[i][j - 1];
                    } else {
                        l[i][j] = j;
                    }
                }
            }
            for (int j = m; j >= 1; --j) {
                if (j == m) {
                    r[i][j] = j;
                } else {
                    int nx = a[i][j + 1], now = a[i][j];
                    if (id[nx].fi == id[now].fi && id[nx].se == id[now].se + 1) {
                        r[i][j] = r[i][j + 1];
                    } else {
                        r[i][j] = j;
                    }
                }
            }
        }
        for (int j = 1; j <= m; ++j) {
            for (int i = 1; i <= n; ++i) {
                if (i == 1) {
                    num[i][j] = 1;
                } else {
                    int pre = a[i - 1][j], now = a[i][j];
                    if (id[pre].fi == id[now].fi - 1 && id[pre].se == id[now].se) {
                        num[i][j] = num[i - 1][j] + 1;
                    } else {
                        num[i][j] = 1;
                    }
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= n; ++i) {
        //  for (int j = 1; j <= m; ++j) {
        //      dbg(i, j, l[i][j], r[i][j], num[i][j]);
        //  }
            ct.init();
            ct.build(n, num[i]);
            ct.dfs(ct.root);
            for (int j = 1; j <= m; ++j) {
                int tl = max(l[i][j], j - c[j].fi);
                int tr = min(r[i][j], j + c[j].se);
                res = max(res, (tr - tl + 1) * num[i][j]);
            }
        }
        printf("%d\n", res);
    }
    return 0;
}

N. Fibonacci Sequence

纯输出题

猜你喜欢

转载自www.cnblogs.com/Dup4/p/11963769.html