2019 ICPC Asia Nanjing Regional F. Paper Grading

题目链接:https://nanti.jisuanke.com/t/42400

题意:给一个模式串集合,序号1~n,有T个操作,或者是交换序号,或者是查询模式串集合中序号在L到R之 间的字符串有多少个和目标串公共前缀长度大于等于K。
—————————————————————————————————
对模式串集合建字典树,则与目标串\(LCP\)大于等K的字符串,都在以目标串第\(K\)个字符所在的结点为根的子树中,每个模式串插入字典树的过程中记录序号,修改时交换序号,询问就相于在子树中询问有多少结点的序号在\(L\)到\(R\)之间。
涉及到子树,容易想到建立\(DFS\)序,再用数据结构维护
但是不建议在字典树的\(DFS\)序上用数据结构维护,因为可能出现两个一样的模式串,但是序号不一样,可能会覆盖之前的序号(个人观点,我写的时候是遇到了这种问题,可能写法问题)
所以可以用一个数组\(h[i]\),表示序号为\(i\)的字符串在字典树中的编号为\(h[i]\),对\(h\)使用数据结构维护,需要支持单点修改(交换视为两次单点修改)和查询区间\([L,R]\)中\(h[i]\)的值在范围\([in[x],out[x]]\)中的数量(\(x\)为目标串第\(K\)字符在字典树中的结点编号)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
int ptov[maxn], tv[maxn];
/************************************************************/
struct Trie
{
    int trie[maxn][26], tot = 0;
    int ins(char s[])
    {
        int len = strlen(s);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = s[i] - 'a';
            if (trie[root][id] == 0)
                trie[root][id] = ++tot;
            root = trie[root][id];
        }
        return root;
    }
    int fin(int k, char s[])
    {
        int root = 0;
        for (int i = 0; i < k; i++)
        {
            int id = s[i] - 'a';
            if (trie[root][id] == 0)
                return -1;
            root = trie[root][id];
        }
        return root;
    }
} t;
/************************************************************/
int in[maxn], out[maxn];
int tim = 0;
void dfs(int x)
{
    in[x] = ++tim;
    for (int i = 0; i < 26; i++)
    {
        if (t.trie[x][i] == 0)
            continue;
        dfs(t.trie[x][i]);
    }
    out[x] = tim;
}
/************************************************************/
int T[maxn], S[maxn], L[maxn * 150], R[maxn * 150], sum[maxn * 150];
int h[maxn];
int ul[maxn], ur[maxn];
int tot, num, n, m;

struct node
{
    int l, r, k;
    bool flag;
} Q[maxn << 1];

void build(int &rt, int l, int r)
{
    rt = ++tot;
    sum[rt] = 0;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    build(L[rt], l, mid);
    build(R[rt], mid + 1, r);
}

void update(int &rt, int pre, int l, int r, int x, int val)
{
    rt = ++tot;
    L[rt] = L[pre];
    R[rt] = R[pre];
    sum[rt] = sum[pre] + val;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    if (x <= mid)
        update(L[rt], L[pre], l, mid, x, val);
    else
        update(R[rt], R[pre], mid + 1, r, x, val);
}

int lowbit(int x) { return x & (-x); }

void add(int x, int val)
{
    int res = lower_bound(h + 1, h + 1 + num, ptov[x]) - h;
    while (x <= n)
    {
        update(S[x], S[x], 1, num, res, val);
        x += lowbit(x);
    }
}

int Sum(int x, int flag)
{
    int res = 0;
    while (x > 0)
    {
        if (flag == 1)
            res += sum[L[ur[x]]];
        else if (flag == 2)
            res += sum[L[ul[x]]];
        else if (flag == 3)
            res += sum[ur[x]];
        else
            res += sum[ul[x]];
        x -= lowbit(x);
    }
    return res;
}
int query(int s, int e, int ts, int te, int l, int r, int k)
{
    if (l == r)
        return Sum(e, 3) - Sum(s, 4) + sum[te] - sum[ts];
    int mid = (l + r) >> 1;
    int res = Sum(e, 1) - Sum(s, 2) + sum[L[te]] - sum[L[ts]];
    if (k <= mid)
    {
        for (int i = e; i; i -= lowbit(i))
            ur[i] = L[ur[i]];
        for (int i = s; i; i -= lowbit(i))
            ul[i] = L[ul[i]];
        return query(s, e, L[ts], L[te], l, mid, k);
    }
    else
    {
        for (int i = e; i; i -= lowbit(i))
            ur[i] = R[ur[i]];
        for (int i = s; i; i -= lowbit(i))
            ul[i] = R[ul[i]];
        return res + query(s, e, R[ts], R[te], mid + 1, r, k);
    }
}
/************************************************************/
char s[maxn];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s", s);
        ptov[i] = t.ins(s);
    }
    dfs(0);
    for (int i = 1; i <= n; i++)
    {
        ptov[i] = in[ptov[i]];
        h[i] = ptov[i];
        tv[i] = ptov[i];
    }
    int q = 0;
    for (int i = 1; i <= m; i++)
    {
        int opt;
        scanf("%d", &opt);
        if (opt == 2) //询问
        {
            scanf("%s%d%d%d", s, &Q[q].k, &Q[q].l, &Q[q].r);
            Q[q].k = t.fin(Q[q].k, s);
            Q[q].flag = true;
            q++;
        }
        else //修改
        {
            int tl, tr;
            scanf("%d%d", &tl, &tr);
            Q[q].l = tl;
            Q[q].r = tv[tr];
            Q[q].flag = false;
            q++;

            Q[q].l = tr;
            Q[q].r = tv[tl];
            Q[q].flag = false;
            q++;

            swap(tv[tl], tv[tr]);
        }
    }
    sort(h + 1, h + 1 + n);
    num = unique(h + 1, h + 1 + n) - h - 1;
    tot = 0;
    build(T[0], 1, num);
    for (int i = 1; i <= n; i++)
        update(T[i], T[i - 1], 1, num, lower_bound(h + 1, h + 1 + num, ptov[i]) - h, 1);
    for (int i = 1; i <= n; i++)
        S[i] = T[0];
    for (int i = 0; i < q; i++)
    {
        if (Q[i].flag)
        {
            if (Q[i].k == -1)
                puts("0");
            else if (Q[i].k == 0)
                printf("%d\n", Q[i].r - Q[i].l + 1);
            else
            {
                int tin = lower_bound(h + 1, h + 1 + num, in[Q[i].k]) - h;
                auto tout = lower_bound(h + 1, h + 1 + num, out[Q[i].k]);
                for (int j = Q[i].r; j; j -= lowbit(j))
                    ur[j] = S[j];
                for (int j = Q[i].l - 1; j; j -= lowbit(j))
                    ul[j] = S[j];
                int ans1 = query(Q[i].l - 1, Q[i].r, T[Q[i].l - 1], T[Q[i].r], 1, num, tout - h);
                if (tin == 1)
                {
                    if (*tout == out[Q[i].k])
                        printf("%d\n", ans1);
                    else
                        printf("%d\n", ans1 - 1);
                    continue;
                }
                for (int j = Q[i].r; j; j -= lowbit(j))
                    ur[j] = S[j];
                for (int j = Q[i].l - 1; j; j -= lowbit(j))
                    ul[j] = S[j];
                int ans2 = query(Q[i].l - 1, Q[i].r, T[Q[i].l - 1], T[Q[i].r], 1, num, tin - 1);
                if (*tout == out[Q[i].k])
                    printf("%d\n", ans1 - ans2);
                else
                    printf("%d\n", ans1 - 1 - ans2);
            }
        }
        else
        {
            add(Q[i].l, -1);
            ptov[Q[i].l] = Q[i].r;
            add(Q[i].l, 1);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Zeronera/p/11997580.html

时间: 2024-08-30 12:23:36

2019 ICPC Asia Nanjing Regional F. Paper Grading的相关文章

2019 ICPC Asia Nanjing Regional 重现赛

C. Digital Path 题意: 给出一个网格,格子里面有各自的值,要求找出有多少条线路,是从某一个块四周自己最小开始走到某一个块,四周自己最大并且长度大于等于四. 解:显然,对于某一块,我们判断他能不能成为起点,就可以判断他的上下左右是否比他小1,比他小1那么肯定不能作为起点.判断终点同理. 那么针对每一个起点,我们首先暴力dfs,判断他的上下左右是否满足比他刚好大1,有的话继续dfs下去. 这样我们得到了一个暴力算法然后考虑优化. 令dp[i][j][len]表示,第(i,j)块,作为

2019 ICPC Asia Yinchuan Regional

目录 Contest Info Solutions A. Girls Band Party B. So Easy D. Easy Problem E. XOR Tree F. Function! G. Pot!! H. Delivery Route I. Base62 K. Largest Common Submatrix N. Fibonacci Sequence Contest Info Practice Link Solved A B C D E F G H I J K L M N 9/1

The 2019 ICPC Asia Shanghai Regional Contest-C-Spanning Tree Removal

知识点:构造.思维. 题目链接:https://ac.nowcoder.com/acm/contest/4370/D 题意:n点完全图,每次可以删除一个生成树的边,问最多可以删几次,并构造出其中一种. 题解:给出一种删边方式可以尽可能多次的删除:第i次:从i开始.依次删除i-(i+1)-(i-1)-(i+2)-(i-2)-…… 直到连接完所有点为止.总共可以删除n/2次. AC代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 i

The 2019 ICPC Asia Shanghai Regional Contest-B-Prefix Code

知识点:字典树. 题目链接: https://ac.nowcoder.com/acm/contest/4370/B 题意:t组数据,n个数字,问是否满足不存在任何一个数字是其他数字的前缀. 题解:套用字典树一个一个插入字符串.若在插入过程中遇到如下两种情况,则存在其中一个是另一个的前缀. 1.遍历完整个字符串都没有创造新的节点,说明该字符串是之前某个串的字串. 2.遍历过程中遇到了某个字符串的终点,说明存在一个字符串是该字符串的前缀. 若都不存在,则满足条件. AC代码: 1 #include

The 2019 ICPC Asia Shanghai Regional Contest---H Tree Partition 二分答案,从下往上切最大子树

题意:https://ac.nowcoder.com/acm/contest/4370 将一棵树切k-1刀分成k棵树,问这k棵树里最大的权值的最小值是多少 思路:https://www.cnblogs.com/ucprer/p/11931263.html 二分最大值. dfs割子树,每次从下往上切一个最大的树. 单个dfs表示保证当前节点可切,然后子树dfs完了,sort子树,选最大的切,直到当前u节点也可切 1 #define IOS ios_base::sync_with_stdio(0);

2018-2019, ICPC, Asia Yokohama Regional Contest 2018 (Gym - 102082)

2018-2019, ICPC, Asia Yokohama Regional Contest 2018 A - Digits Are Not Just Characters 签到. B - Arithmetic Progressions 题意:从给定的集合中选出最多的数构成等差数列. 题解:数字排序后,设\(dp[i][j]\)表示等差数列最后一个数字为\(a[i]\),倒数第二个数字为\(a[j]\)的最大个数.然后对于每一位枚举 \(i\),\(lower\_bound()\)找有无合法的

2018 ICPC Asia Singapore Regional A. Largest Triangle (计算几何)

题目链接:Kattis - largesttriangle Description Given \(N\) points on a \(2\)-dimensional space, determine the area of the largest triangle that can be formed using \(3\) of those \(N\) points. If there is no triangle that can be formed, the answer is \(0\

2019南京ICPC(重现赛) F - Paper Grading

题目链接:https://nanti.jisuanke.com/t/42400 这还是去年去现场赛打的,当时菜的不行,就白给了.最近学了主席树套树状数组,感觉好强的数据结构啊.我们学长说这题挺简单,建字典树dfs序,跑cdq分治就好了(%%%).本菜鸡发现这题主席树套树状数组也能做. 题意:给你n个字符串,m个操作.操作1交换俩个字符串,操作2,给定一个字符串s,数字k, l, r, 求l ~ r与s公共前缀大于等于k的字符串的个数. 首先考虑没有修改,那么就只有操作2,设f[i]为第i个字符串

The Preliminary Contest for ICPC Asia Nanjing 2019/2019南京网络赛——题解

(施工中……) 比赛传送门:https://www.jisuanke.com/contest/3004 D. Robots(期望dp) 题意 给一个DAG,保证入度为$0$的点只有$1$,出度为$0$的点只有$n$. 现在一个机器人从$1$出发,每天都会以相同的概率前往相邻节点之一或静止不动. 每天机器人消耗的耐久等于经过的天数. 求机器人到点$n$期望消耗的耐久. 划水划的很愉快,唯一一道做出来的题.但是和题解做法不同(感觉我的方法麻烦),因此砸了3h在这题上面(正在试图读懂题解ing). 设