The Preliminary Contest for ICPC Asia Xuzhou 2019

A What is better?

推不出来,写个程序打表,用扩展中国剩余定理合并,居然会溢出longlong,还好不会溢出__int128(赛后exit(-1)测试),实际证明溢出返回-1是不靠谱的,毕竟后面可以又把它搞小了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 lll;

const int MAXK = 10 + 5;

void exgcd(lll a, lll b, lll &x, lll &y) {
    if(!b)
        x = 1, y = 0;
    else
        exgcd(b, a % b, y, x), y -= a / b * x;
}

lll inv(ll a, ll b) {
    lll x = 0, y = 0;
    exgcd(a, b, x, y);
    x = (x % b + b) % b;
    if(!x)
        x += b;
    return x;
}

int k;
lll c[MAXK], m[MAXK];

lll exCRT(int k) {
    lll c1, c2, m1, m2, t;
    for(int i = 2; i <= k; ++i) {
        m1 = m[i - 1], m2 = m[i], c1 = c[i - 1], c2 = c[i];
        t = __gcd(m1, m2);
        if((c2 - c1) % t != 0)
            return -1;
        m[i] = m1 / t * m2;
        if(m[i] <= 0)
            exit(-1);
        c[i] = inv(m1 / t, m2 / t) * ((c2 - c1) / t) % (m2 / t) * m1 + c1;
        c[i] = (c[i] % m[i] + m[i]) % m[i];
    }
    return c[k];
}

/*map<pair<int, int>, pair<bool, int> > M;

bool dfs(int id, int pre) {
    if(M.count({id, pre}))
        return M[ {id, pre}].first;
    if(pre == -1) {
        for(int i = 1; i < id; ++i) {
            if(dfs(id - i, i) == false) {
                M[{id, pre}] = {true, i};
                return true;
            }
        }
        M[{id, pre}] = {false, -1};
        return false;
    } else {
        int c = min(2 * pre, id);
        if(c == id) {
            M[{id, pre}] = {true, c};
            return true;
        }

        for(int i = 1; i <= c; ++i) {
            if(dfs(id - i, i) == false) {
                M[{id, pre}] = {true, i};);
                return true;
            }
        }
        M[{id, pre}] = {false, -1};
        return false;
    }
}*/

ll f[72 + 5];

int main() {
#ifdef local
    freopen("lyz.in", "r", stdin);
#endif // local
    /*for(int i = 2; i <= 100; ++i) {
        printf("i=%d", i);
        if(dfs(i, -1)) {
            printf(" WIN
");
            printf(" TO TAKE %d
", M[ {i, -1}].second);
        } else
            printf(" FAIL
");
    }

    for(int j = 1; j <= 20; ++j) {
        for(int k = -1; k <= 20; ++k) {
            if(M.count({j, k})) {
                printf("(%d,%d) 
", j, k);
                if(M[ {j, k}].first == true) {
                    printf(" WIN
");
                    printf(" TO TAKE %d
", M[ {j, k}].second);
                } else {
                    printf(" FAIL
");
                }
                puts("");
            }
        }
    }*/

    int k;
    scanf("%d", &k);
    bool suc = 1;
    for(int i = 1; i <= k; ++i) {
        ll tmp1, tmp2;
        scanf("%lld%lld", &tmp1, &tmp2);
        m[i] = tmp1;
        c[i] = tmp2;
        if(c[i] > 1e15) {
            suc = false;
        }
    }

    if(!suc) {
        puts("Tankernb!");
        return 0;
    }

    lll n = exCRT(k);
    if(n <= 1 || n > 1e15) {
        puts("Tankernb!");
        return 0;
    }
    f[1] = 2;
    f[2] = 3;
    for(int i = 3; i <= 72; ++i) {
        f[i] = f[i - 1] + f[i - 2];
        if(f[i] >= 1e15) {
            //cout<<"i="<<i<<endl;
            break;
        }
    }
    for(int i = 1; i <= 72; ++i) {
        if(n == f[i]) {
            puts("Lbnb!");
            return 0;
        }
    }
    puts("Zgxnb!");
    return 0;
}

B so easy

一开始1e6弄个set莽了两次,果断T了,事实证明平衡树的常数的确相比离散化是在是太大了。用个并查集维护,删除一个节点的时候,假如他没有被删除过,那么就把他指向他的下一个元素(无论他的下一个元素是不是被删了都可以),并且把这个元素设置为“删除”,下面参照非递归路径压缩并查集弄了一个非递归路径压缩,实测727ms还算可以。

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

inline int read() {
    int x = 0;
    char ch = getchar();
    while(ch < '0' || ch > '9')
        ch = getchar();
    do {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    } while(ch >= '0' && ch <= '9');
    return x;
}

const int MAXN = 1e6;
int z[MAXN + 5], x[MAXN + 5];
int px[2 * MAXN + 5], xtop;

int nxt[2 * MAXN + 5];
bool vis[2 * MAXN + 5] = {};

inline int find(int i) {
    int cur = nxt[i];
    while(vis[cur]) {
        cur = nxt[cur];
    }
    while(nxt[i] != cur) {
        int t = nxt[i];
        nxt[i] = cur;
        i = t;
    }
    return cur;
}

int main() {
#ifdef local
    freopen("lyz.in", "r", stdin);
#endif // local
    int n = read(), q = read();
    xtop = 0;
    for(int i = 1; i <= q; ++i) {
        z[i] = read(), x[i] = read();
        px[++xtop] = x[i];
        if(z[i] == 1)
            px[++xtop] = x[i] + 1;
    }
    sort(px + 1, px + 1 + xtop);
    xtop = unique(px + 1, px + 1 + xtop) - (px + 1);
    for(int i = 1; i <= q; ++i)
        x[i] = lower_bound(px + 1, px + 1 + xtop, x[i]) - (px);

    for(int i = 1; i <= xtop; ++i)
        nxt[i] = i;

    for(int i = 1; i <= q; ++i) {
        if(z[i] == 1) {
            if(!vis[x[i]]) {
                vis[x[i]] = true;
                nxt[x[i]] = nxt[x[i] + 1];
                find(x[i]);
            }
        } else {
            int res = find(x[i]);
            printf("%d
", px[res]);
        }
    }

}

J Random Access Iterator

一个逗逼签到题,根据期望的线性性蛮好推的,首先第一遍dfs算出最大深度和各个节点的深度(虽然各个节点的深度是不必要的),第二遍dfs把dp数组算出来。设dp[i]表示i的子树给出最大深度的概率。那么对于深度为最大深度的叶子节点,dp[i]=1,其他叶子dp[i]=0。那么对于中间节点,它的所有k个子节点能提供最大深度的概率的期望就是各个子节点的dp值的平均值。它不能给出正确深度的概率就是这个期望的补数连续乘k次。

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

const int MOD = 1e9 + 7;

const int MAXN = 1e6;
vector<int> G[MAXN + 5];

int qpow(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

int dp[MAXN + 5];

int MAXD;

void dfs1(int u, int p, int d) {
    MAXD = max(MAXD, d);
    for(auto v : G[u]) {
        if(v != p) {
            dfs1(v, u, d + 1);
        }
    }
    return;
}

void dfs2(int u, int p, int d) {
    if(d == MAXD) {
        dp[u] = 1;
        return;
    }
    ll P = 0;
    int n = 0;
    for(auto v : G[u]) {
        if(v != p) {
            dfs2(v, u, d + 1);
            P += dp[v];
            ++n;
        }
    }
    P = P * qpow(n, MOD - 2) % MOD;
    ll Q = (1ll - P + MOD) % MOD;
    Q = qpow(Q, n);
    dp[u] = (1ll - Q + MOD) % MOD;
    return;
}

int main() {
#ifdef local
    freopen("lyz.in", "r", stdin);
#endif // local
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n - 1; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }

    dfs1(1, -1, 1);
    dfs2(1, -1, 1);

    printf("%d
", dp[1]);
}

M Longest subsequence

设dp[i]表示匹配t串的长度为i的前缀需要用到s的最短长度,特别地为了统一,dp[0]=0,INF表示无法匹配。设pos[i][ch]表示在s串的i位置及其以后第一个ch出现的下标。

那么只有两种情况,第一,匹配了长度为i的前缀,然后从第i+1个字符开始严格大,i从0开始,这样就暴力一遍比t[i+1]大的最近的pos就行了。第二,完全匹配t,然后后面有多少加多少,注意至少要加一个

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

const int INF = 1e9;
const int MAXN = 1e6;
char s[MAXN + 5], t[MAXN + 5];
int dp[MAXN + 5];

int pos[MAXN + 5][26];

int main() {
#ifdef local
    freopen("lyz.in", "r", stdin);
#endif // local
    int n, m;
    while(~scanf("%d%d", &n, &m)) {
        scanf("%s%s", s + 1, t + 1);
        dp[0] = 0;
        for(int i = 1; i <= m; ++i)
            dp[i] = INF;
        for(int i = 1; i <= m; ++i) {
            for(int j = dp[i - 1] + 1; j <= n; ++j) {
                if(s[j] == t[i]) {
                    dp[i] = j;
                    break;
                }
            }
            if(dp[i] == INF)
                break;
        }

        for(int i = 0; i < 26; ++i)
            pos[n + 1][i] = INF;
        for(int i = n; i >= 1; --i) {
            for(int j = 0; j < 26; ++j)
                pos[i][j] = pos[i + 1][j];
            pos[i][s[i] - 'a'] = i;
        }

        int ans = -1;

        for(int i = 0; i <= m; ++i) {
            int last = INF;
            if(dp[i] == INF)
                break;
            else {
                if(i == m) {
                    if(dp[i] != n)
                        ans = max(ans, i + n - (dp[i] + 1) + 1);
                } else {
                    for(int j = t[i + 1] - 'a' + 1; j < 26; ++j)
                        last = min(last, pos[dp[i] + 1][j]);
                    ans = max(ans, i + n - last + 1);
                }
            }
        }
        printf("%d
", ans);
    }
}
原文地址:https://www.cnblogs.com/Inko/p/11483758.html