The Preliminary Contest for ICPC Asia Xuzhou 2019 部分题解

B. so easy

解法:我们离线写这题,每次更新一个x,我们取x,x + 1两个点存着,最后把存的点离散化建线段树,然后每次查询线段树区间最小值即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
int mn[maxn * 4], a[maxn], inf = 2e9;
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void build(int o, int l, int r) {
    if (l == r) {
        mn[o] = a[l];
        return;
    }
    build(ls, l, m);
    build(rs, m + 1, r);
    mn[o] = min(mn[ls], mn[rs]);
}
void up(int o, int l, int r, int k) {
    if (l == r) {
        mn[o] = inf;
        return;
    }
    if (k <= m)
        up(ls, l, m, k);
    else
        up(rs, m + 1, r, k);
    mn[o] = min(mn[ls], mn[rs]);
}
int qu(int o, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr)
        return mn[o];
    int res = inf;
    if (ql <= m)
        res = min(res, qu(ls, l, m, ql, qr));
    if (qr > m)
        res = min(res, qu(rs, m + 1, r, ql, qr));
    return res;
}
int z[maxn], x[maxn];
int main() {
    int n, q, sz = 0;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= q; i++) {
        scanf("%d%d", &z[i], &x[i]);
        a[++sz] = x[i];
        if (x[i] < n)
            a[++sz] = x[i] + 1;
    }
    sort(a + 1, a + 1 + sz);
    sz = unique(a + 1, a + 1 + sz) - a - 1;
    for (int i = 1; i <= q; i++)
        x[i] = lower_bound(a + 1, a + 1 + sz, x[i]) - a;
    build(1, 1, sz);
    for (int i = 1; i <= q; i++)
    if (z[i] == 1)
        up(1, 1, sz, x[i]);
    else {
        int ans = qu(1, 1, sz, x[i], sz);
        if (ans == inf)
            ans = -1;
        printf("%d\n", ans);
    }
}

G. Colorful String

解法:我们建立回文树,用bitset存每个回文子串用了哪些字母,然后设dp[i]为以 i 结尾的所有回文子串的所有字母种类数,我们通过fail指针找到 i 的fail节点 j,显然dp[i] = dp[j] + 以 i 结尾的最长回文子串种类数。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e5 + 10;
ll ans;
int dp[maxn];
bitset<26> cat[maxn];
struct ptree{
    char s[maxn];
    int next[maxn][26],fail[maxn],cnt[maxn],len[maxn];
    int last,n,p;
    long long res;
    inline int newnode(int l){
        cnt[p]=0;
        len[p]=l;
        return p++;
    }
    inline void init(){
        newnode(0),newnode(-1);//two roots
        s[n]=-1;
        fail[0]=1;
    }
    inline int FL(int x){
        while(s[n-len[x]-1]!=s[n]) x=fail[x];
        return x;
    }
    void add(char c){
        c-='a';
        s[++n]=c;
        int cur=FL(last);
        if(!next[cur][c]){
            int now=newnode(len[cur]+2);
            cat[now] = cat[cur];
            cat[now][c] = 1;
            fail[now]=next[FL(fail[cur])][c];
            next[cur][c]=now;
        }
        last=next[cur][c];
        dp[last] = cat[last].count() + dp[fail[last]];
        ans += dp[last];
        ++cnt[last];
    }
} p;
char s[maxn];
int main() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    p.init();
    for (int i = 1; i <= n; i++)
        p.add(s[i]);
    printf("%lld\n", ans);
}

I. query

解法:我们知道排列中所有的gcd(i, j) = min(i, j)的数量在nlogn级别,因此我们可以找到所有 pi 的因子所在的位置,然后根据这个建立可持久化线段树,然后直接查询即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 1;
int rt[maxn], ls[maxn * 190], rs[maxn * 190], sum[maxn * 190], cnt;
int a[maxn], p[maxn], ans[maxn];
vector<int> G[maxn];
#define mid (l + r) / 2
void up(int &o, int pre, int l, int r, int k) {
    o = ++cnt;
    ls[o] = ls[pre];
    rs[o] = rs[pre];
    sum[o] = sum[pre] + 1;
    if (l == r)
        return;
    if (k <= mid)
        up(ls[o], ls[pre], l, mid, k);
    else
        up(rs[o], rs[pre], mid + 1, r, k);
}
int qu(int o, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr)
        return sum[o];
    int res = 0;
    if (ql <= mid)
        res += qu(ls[o], l, mid, ql, qr);
    if (qr > mid)
        res += qu(rs[o], mid + 1, r, ql, qr);
    return res;
}
int l[maxn], r[maxn];
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        p[a[i]] = i;
    }
    for (int i = 1; i < n; i++)
    for (int j = i * 2; j <= n; j += i)
    if (p[i] < p[j])
        G[p[j]].push_back(p[i]);
    for (int i = 1; i <= n; i++) {
        rt[i] = rt[i - 1];
        for (auto x : G[i])
            up(rt[i], rt[i], 1, n, x);
        G[i].resize(0);
    }
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &l[i], &r[i]);
        ans[i] += qu(rt[r[i]], 1, n, l[i], n);
    }
    cnt = 0;
    for (int i = 1; i < n; i++)
    for (int j = i * 2; j <= n; j += i)
    if (p[i] > p[j])
        G[p[i]].push_back(p[j]);

    for (int i = 1; i <= n; i++) {
        rt[i] = rt[i - 1];
        for (auto x : G[i])
            up(rt[i], rt[i], 1, n, x);
        G[i].resize(0);
    }
    for (int i = 1; i <= m; i++)
        ans[i] += qu(rt[r[i]], 1, n, l[i], n);
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
}

J. Random Access Iterator

解法:正难则反,我们设dp[u]为从u点出发到不了最深的叶子节点概率,设sz[u]为u节点儿子数量,不难想到 d p [ u ] = ( 1 s z [ u ] ∑ s o n d p [ s o n ] ) s z [ u ] dp[u]=(\frac{1}{sz[u]}\sum_{son}dp[son])^{sz[u]} dp[u]=(sz[u]1sondp[son])sz[u]
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 10, mod = 1e9 + 7;
vector<int> G[maxn];
int sz[maxn], dep[maxn], mx, d[maxn];
void add(int &x, int y) {
    x += y;
    if (x >= mod)
        x -= mod;
    if (x < 0)
        x += mod;
}
ll ksm(ll x, int y) {
    ll res = 1;
    while (y) {
        if (y & 1)
            res = res * x % mod;
        x = x * x % mod;
        y /= 2;
    }
    return res;
}
void dfs(int u, int fa) {
    dep[u] = dep[fa] + 1;
    mx = max(mx, dep[u]);
    for (auto v : G[u])
    if (v != fa) {
        sz[u]++;
        dfs(v, u);
    }
}
int dfs2(int u, int fa) {
    if (!sz[u])
        return (mx != dep[u]);
    int inv = ksm(sz[u], mod - 2), ans = 0;
    for (auto v : G[u])
        if (v != fa)
            ans = (ans + 1ll * inv * dfs2(v, u) % mod) % mod;
    return ksm(ans, sz[u]);
}
int main() {
    int n, u, v;
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    int ans = 1 - dfs2(1, 0);
    add(ans, 0);
    printf("%d\n", ans);
}

M. Longest subsequence

解法:设 d p [ i ] [ c ] = p dp[i][c] = p dp[i][c]=p s s s串前 i − 1 i - 1 i1个字符,下一次即将匹配 t t t串最大的下标 p p p满足 t [ p ] = c t[p] = c t[p]=c,假如 s [ i ] = c s[i] = c s[i]=c,那么显然对于所有 j &lt; c j &lt; c j<c a n s = m a x ( a n s , d p [ i ] [ j ] − 1 + n − i + 1 ) ans = max(ans, dp[i][j] - 1 + n - i + 1) ans=max(ans,dp[i][j]1+ni+1),当然我也可以继续相等的匹配,令 x = d p [ i ] [ c ] + 1 x = dp[i][c] + 1 x=dp[i][c]+1 d p [ i + 1 ] [ t [ x ] ] = m a x ( d p [ i + 1 ] [ t [ x ] ] , d p [ i ] [ x ] + 1 ) dp[i + 1][t[x]] =max(dp[i + 1][t[x]], dp[i][x] + 1) dp[i+1][t[x]]=max(dp[i+1][t[x]],dp[i][x]+1)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10, inf = 1e9;
char s[maxn], t[maxn];
int dp[maxn][26];
int main() {
    int n, len;
    scanf("%d%d", &n, &len);
    scanf("%s%s", s + 1, t + 1);
    int cat = - 1;
    for (int i = 0; i < 26; i++)
        dp[0][i] = -inf;
    dp[0][t[1] - 'a'] = 1;
    for (int i = 1; i <= n; i++) {
        int c = s[i] - 'a';
        for (int j = 0; j < 26; j++)
            dp[i][j] = dp[i - 1][j];
        for (int j = c - 1; ~j; j--)
            cat = max(cat, dp[i][j] - 1 + n - i + 1);
        if (dp[i][c] == len && i < n)
            cat = max(cat, dp[i][c] - 1 + n - i + 1);
        if (dp[i][c] != -inf && dp[i][c] < len) {
            int x = dp[i][c] + 1;
            int cc = t[x] - 'a';
            dp[i][cc] = max(dp[i][cc], dp[i][c] + 1);
        }
    }
    printf("%d\n", cat);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值