2019-2020 ICPC, Asia Jakarta Regional Contest 部分题解

E. Songwriter

题意:给你一个序列 A A A,你要构造一个字典序最小的序列 B B B,满足构造元素取值范围为 [ l , r ] [l, r] [l,r],且相邻元素差值绝对值不超过 K K K,且相邻元素的大小关系和序列 A A A对应元素保持一致。
解法:这个题感觉以前做过,但是这次还是没做出来…我们记 L i , R i L_{i},R_{i} Li,Ri分别为 B i B_{i} Bi的合法取值范围,我们倒推 B B B序列,初始化 L n = l , R n = r L_{n}=l,R_{n}=r Ln=l,Rn=r,如果 A i > A i + 1 A_{i}>A_{i+1} Ai>Ai+1那么 R i = m i n ( r , R i + 1 + k ) , L i = L i + 1 + 1 R_{i}=min(r,R_{i+1}+k),L_{i}=L_{i+1}+1 Ri=min(r,Ri+1+k),Li=Li+1+1,如果 A i < A i + 1 A_{i}Ai<Ai+1,那么 R i = R i + 1 − 1 , L i = m a x ( l , L i + 1 − k ) R_{i}=R_{i+1}-1,L_{i}=max(l,L_{i+1}-k) Ri=Ri+11,Li=max(l,Li+1k),求出 L i , R i L_{i},R_{i} Li,Ri后顺推去构造即可

#include 
using namespace std;
const int maxn = 1e5 + 10;
int L[maxn], R[maxn], a[maxn], b[maxn];
int main()
{
     
    int n, l, r, k;
    scanf("%d%d%d%d", &n, &l, &r, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    L[n] = l, R[n] = r;
    for (int i = n - 1; i; i--)
    {
     
        if (a[i] > a[i + 1])
        {
     
            R[i] = min(r, R[i + 1] + k);
            L[i] = L[i + 1] + 1;
        }
        else if (a[i] < a[i + 1])
        {
     
            L[i] = max(l, L[i + 1] - k);
            R[i] = R[i + 1] - 1;
        }
        else
            L[i] = L[i + 1], R[i] = R[i + 1];
        if (L[i] > R[i])
            return puts("-1"), 0;
    }
    int x = L[1];
    printf("%d", L[1]);
    for (int i = 2; i <= n; i++)
    {
     
        if (a[i] > a[i - 1])
            x = max(x + 1, L[i]);
        else if (a[i] < a[i - 1])
            x = max(L[i], x - k);
        printf(" %d", x);
    }
}

F. Regular Forestation

题意:给一棵树,求一个度数最大点,满足度数大于1,然后这个点为根的所有子树的结构相同,输出度数
解法:裸的树hash,但是要枚举所有点,那么再搞个换根dp求所有点为根的树hash即可,复杂度nlogn
#include 
#define ll long long
using namespace std;
const int maxn = 4005, mod1 = 998244353, mod2 = 1e9 + 7;
vector<int> G[maxn];
int p1[maxn], p2[maxn];
struct Hash
{
     
    int h1, h2, len;
} d[maxn];
vector<Hash> S[maxn];
int ans = -1;
bool cmp(int a, int b)
{
     
    return d[a].h1 < d[b].h1 || (d[a].h1 == d[b].h1 && d[a].h2 < d[b].h2);
}
Hash merge(Hash a, Hash b)
{
     
    Hash jie = Hash{
     0, 0, a.len + b.len};
    jie.h1 = (1ll * a.h1 * p1[b.len] + b.h1) % mod1;
    jie.h2 = (1ll * a.h2 * p2[b.len] + b.h2) % mod2;
    return jie;
}
void dfs(int u, int fa)
{
     
    d[u] = Hash{
     1, 1, 1};
    for (auto v : G[u])
        if (v != fa)
            dfs(v, u);
    sort(G[u].begin(), G[u].end(), cmp);
    for (auto v : G[u])
        if (v != fa)
            d[u] = merge(d[u], d[v]);
}
void dfs2(int u, int fa)
{
     
    d[u] = Hash{
     1, 1, 1};
    sort(G[u].begin(), G[u].end(), cmp);
    Hash cat{
     0, 0, 0};
    int jie = 1;
    S[u].push_back(Hash{
     1, 1, 1});
    for (auto v : G[u])
    {
     
        d[u] = merge(d[u], d[v]);
        if (cat.len == 0)
            cat = d[v];
        else if (cat.h1 != d[v].h1 || cat.h2 != d[v].h2)
                jie = 0;
        S[u].push_back(d[u]);
    }
    if (jie && G[u].size() > 1)
        ans = max(ans, (int)G[u].size());
    Hash nvshen{
     0, 0, 0};
    for (int i = G[u].size() - 1; ~i; i--)
    {
     
        d[u] = merge(S[u][i], nvshen);
        nvshen = merge(d[G[u][i]], nvshen);
        if (G[u][i] != fa)
            dfs2(G[u][i], u);
    }
}
int main()
{
     
    p1[0] = p2[0] = 1;
    for (int i = 1; i < maxn; i++)
    {
     
        p1[i] = 1ll * p1[i - 1] * 199967 % mod1;
        p2[i] = 1ll * p2[i - 1] * 199999 % mod2;
    }
    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);
    dfs2(1, 0);
    printf("%d\n", ans);
}

G. Performance Review

题意:给出 n n n个人能力值,你是第一个人,然后给出 m m m次变动,每次变动公司开除能力值最低的 k k k个人,保证每个人能力值互不相同,然后加入 k k k个人,并给出他们的能力值,有 q q q次操作,每次操作修改第 i i i次变动中加入的第 j j j个人的能力值,然后问你在 m m m次变动后会不会被开除,每次操作的修改是永久修改(对下一次操作有影响)
解法:我们记 p i p_{i} pi为前 i i i次变动中能力值比我低的人的总数量, s u m i sum_{i} sumi为前 i i i次操作中开除的人的数量总和,用线段树维护 p i − s u m i p_{i}-sum_{i} pisumi的最小值即可,如果最小值小于0,那肯定就被开除
#include 
using namespace std;
const int maxn = 1e5 + 10;
int mn[maxn * 4], tag[maxn * 4], a[maxn];
vector<int> b[maxn];
#define ls o * 2
#define rs o * 2 + 1
#define mid (l + r) / 2
void pushdown(int o)
{
     
    if (tag[o])
    {
     
        tag[ls] += tag[o];
        tag[rs] += tag[o];
        mn[ls] += tag[o];
        mn[rs] += tag[o];
        tag[o] = 0;
    }
}
void up(int o, int l, int r, int ql, int qr, int v)
{
     
    if (l >= ql && r <= qr)
    {
     
        mn[o] += v;
        tag[o] += v;
        return;
    }
    pushdown(o);
    if (ql <= mid)
        up(ls, l, mid, ql, qr, v);
    if (qr > mid)
        up(rs, mid + 1, r, ql, qr, v);
    mn[o] = min(mn[ls], mn[rs]);
}
int main()
{
     
    int n, m, q, res = 0, sum = 0, k, x, v;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i++)
    {
     
        scanf("%d", &a[i]);
        if (i > 1 && a[i] < a[1])
            res++;
    }
    for (int i = 1; i <= m; i++)
    {
     
        scanf("%d", &k);
        sum += k;
        up(1, 1, m, i, i, res - sum);
        for (int j = 1; j <= k; j++)
        {
     
            scanf("%d", &x);
            if (x < a[1])
                res++;
            b[i].push_back(x);
        }
    }
    while (q--)
    {
     
        scanf("%d%d%d", &k, &x, &v);
        if (b[k][x - 1] > a[1])
        {
     
            if (v < a[1] && k < m)
                up(1, 1, m, k + 1, m, 1);
        }
        else
        {
     
            if (v > a[1] && k < m)
                up(1, 1, m, k + 1, m, -1);
        }
        b[k][x - 1] = v;
        if (mn[1] < 0)
            puts("0");
        else
            puts("1");
    }
}

J. Tiling Terrace
待补,怀疑网上部分题解的复杂度…
K. Addition Robot

题意:题面说的很清楚
解法:裸的线段树维护矩阵乘法
#include 
#define ll long long
#define ls o * 2
#define rs o * 2 + 1
#define mid (l + r) / 2
using namespace std;
const int maxn = 1e5 + 10, mod = 1e9 + 7;
void add(int &x, int y)
{
     
    x += y;
    if (x >= mod)
        x -= mod;
    if (x < 0)
        x += mod;
}
struct seg
{
     
    int A[2][2], B[2][2], tag;
    seg operator+(const seg &t) const
    {
     
        seg jie;
        jie.tag = 0;
        memset(jie.A, 0, sizeof(jie.A));
        memset(jie.B, 0, sizeof(jie.B));
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                for (int k = 0; k < 2; k++)
                {
     
                    add(jie.A[i][j], 1ll * A[i][k] * t.A[k][j] % mod);
                    add(jie.B[i][j], 1ll * B[i][k] * t.B[k][j] % mod);
                }
        return jie;
    }
} cat[maxn * 4];
char s[maxn];
void build(int o, int l, int r)
{
     
    if (l == r)
    {
     
        cat[o].A[0][0] = 1, cat[o].A[0][1] = 0, cat[o].A[1][0] = 1, cat[o].A[1][1] = 1;
        cat[o].B[0][0] = 1, cat[o].B[0][1] = 1, cat[o].B[1][0] = 0, cat[o].B[1][1] = 1;
        if (s[l] == 'B')
            swap(cat[o].A, cat[o].B);
        return;
    }
    build(ls, l, mid);
    build(rs, mid + 1, r);
    cat[o] = cat[ls] + cat[rs];
}
void pushdown(int o)
{
     
    if (cat[o].tag)
    {
     
        swap(cat[ls].A, cat[ls].B);
        cat[ls].tag ^= 1;
        swap(cat[rs].A, cat[rs].B);
        cat[rs].tag ^= 1;
        cat[o].tag = 0;
    }
}
void up(int o, int l, int r, int ql, int qr)
{
     
    if (l >= ql && r <= qr)
    {
     
        swap(cat[o].A, cat[o].B);
        cat[o].tag ^= 1;
        return;
    }
    pushdown(o);
    if (ql <= mid)
        up(ls, l, mid, ql, qr);
    if (qr > mid)
        up(rs, mid + 1, r, ql, qr);
    cat[o] = cat[ls] + cat[rs];
}
seg qu(int o, int l, int r, int ql, int qr)
{
     
    if (l >= ql && r <= qr)
        return cat[o];
    pushdown(o);
    if (qr <= mid)
        return qu(ls, l, mid, ql, qr);
    else if (ql > mid)
        return qu(rs, mid + 1, r, ql, qr);
    else
        return qu(ls, l, mid, ql, qr) + qu(rs, mid + 1, r, ql, qr);
}
int main()
{
     
    int n, q, opt, l, r, a, b;
    scanf("%d%d%s", &n, &q, s + 1);
    build(1, 1, n);
    while (q--)
    {
     
        scanf("%d%d%d", &opt, &l, &r);
        if (opt == 1)
            up(1, 1, n, l, r);
        else
        {
     
            scanf("%d%d", &a, &b);
            seg jie = qu(1, 1, n, l, r);
            ll ans1 = 1ll * a * jie.A[0][0] + 1ll * b * jie.A[1][0];
            ll ans2 = 1ll * a * jie.A[0][1] + 1ll * b * jie.A[1][1];
            printf("%lld %lld\n", ans1 % mod, ans2 % mod);
        }
    }
}

你可能感兴趣的:(比赛----gym,数据结构----线段树,动态规划)