• 省选模拟测试21


    wdnmd,真就全都不会做呗。

    T1 字符串值

    题意描述

    定义 (S[l:r]) 表示字符串 (S) 中第 (l) 个到第 (r) 个字符构成的子串,(|S|) 表示字符串 (S) 的长度。

    对于字符串 (S),我们定义 (fail_i) 等于最大的 (x),满足 (x<i)(s[1:x] = s[i-x+1:i]) 。将每个 (0leq ileq |S|) 看成结点,除 (i=0) 以外,(i)(fail_i) 连边,会形成一颗树,根是 (0)(根深度默认为 (-1)),我们将其称为 (fail)

    定义 (F(S))(S)(fail) 树上的除了 (0) 号节点外所有节点的深度和。定义 (displaystyle G(S) = sum_{1leq lleq rleq |S|} F(S[l:r])),即对于 (S) 所有子串的 (F) 求和。

    一开始有一个空串 (S),接下来有 (n) 次操作,每次在空串末尾添加一个小写字母,要求你能够快速求出 。由于答案很大,请对 (1e9+7) 取模。

    数据范围:(nleq 2 imes 10^5)

    solution

    后缀自动机套树链剖分。

    对于一个字符串 (s), 他的 (fail) 树上的一个节点 (i), 对应的是 (s) 的一个前缀,即 (s[1....i])(i) 的深度则表示有多少个 (j) 满足 (s[1...j] = s[i-j+1...i]) 。那么 (F(s)) 则表示 (s) 的前缀的所有符合条件的 (j) 的数量。

    根据这个我们考虑 (G(s)) 怎么算,假设 (s) 的两个子串 (s_1,s_2) 相等,对应的区间分别为:([l_1,r_1],[l_2,r_2])即:

    不难发现 (s[l_1,r_2]) 有一个符合条件的 (j) 使得:(s_1 = s_2) 。同时 (s[l_1,r_2]) 可以作为 (s[l_1,r]) ((r_2leq rleq len(s)))前缀。所以 (s[l_1,r_2])(G(s)) 的贡献则为:(len(s)-r_2+1)

    对于这道题,我们考虑离线处理。先求出以 (i) 为右端点的 (s_2) 满足条件的 ((s_1,s_2)) 的数量,设其为 (h(i)), 求一遍前缀和就可以得到以 (i) 为右端点的字符串的 (F(s)) 的和,在求一遍前缀和就可以得到 (G(s[1..i]))

    先用后缀自动机建出 (partent) 树来。然后考虑每加入一个字符串产生的贡献。

    设当前字符串 (s) 在后缀自动机上对应的节点为 (x) ,那么 (x) 到根节点的路径上的点就可以表示 (s) 的所有后缀。

    显然这些后缀都可以作为 (s_2) ,对应的 (s_1) 的个数即为 (s_2)(s[1...i-1]) 出现的次数。

    (cnt(u)) 表示没加入第 (i) 个字符之前的 (u)(endpos) 的大小。

    则有:(h(i) = displaystylesum_{uin{x的祖先}} cnt(u) imes (maxlen(u)-maxlen(fa(u))))

    (maxlen(u)) 在建后缀自动机的时候就可以统计出来。

    (cnt(u)) 表示的是 (u) 的子树中 (np) 节点的个数,每次把 (x) 到根节点的路径上的点的 (cnt(u)+1) 即可。

    用线段树和树链剖分就可以快速维护出 (cut(u))

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int p = 1e9+7;
    const int N = 4e5+10;
    int n,m,tot,cnt,last,num;
    int head[N],len[N],link[N],tr[N][26],st[N];
    int dep[N],fa[N],top[N],siz[N],son[N],ans[N],dfn[N],id[N];
    char s[N];
    struct node
    {
        int to,net;
    }e[N<<1];
    struct Tree
    {
        int lc,rc;
        int sum,w,tag;
    }Tr[N<<2];
    #define l(o) Tr[o].lc
    #define r(o) Tr[o].rc
    #define sum(o) Tr[o].sum
    #define w(o) Tr[o].w
    #define tag(o) Tr[o].tag
    void add(int x,int y)
    {
        e[++tot].to = y;
        e[tot].net = head[x];
        head[x] = tot;
    }
    void Extend(int ch)
    {
        int cur = ++cnt, p = last;
        len[cur] = len[last] + 1;
        for(; p && !tr[p][ch]; p = link[p]) tr[p][ch] = cur;
        if(!p) link[cur] = 1;
        else
        {
            int x = tr[p][ch];
            if(len[x] == len[p] + 1) link[cur] = x;
            else
            {
                int y = ++cnt;
                len[y] = len[p] + 1;
                memcpy(tr[y],tr[x],sizeof(tr[x]));
                link[y] = link[x]; 
                link[x] = link[cur] = y;
                while(p && tr[p][ch] == x)
                {
                    tr[p][ch] = y;
                    p = link[p];
                }
            } 
        }
        last = cur;
    }
    void get_tree(int x)
    {
        dep[x] = dep[fa[x]] + 1; siz[x] = 1;
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa[x]) continue;
            fa[to] = x;
            get_tree(to);
            siz[x] += siz[to];
            if(siz[to] > siz[son[x]]) son[x] = to;
        }
    }
    void dfs(int x,int topp)
    {
        top[x] = topp; dfn[x] = ++num; id[dfn[x]] = x;
        if(son[x]) dfs(son[x],topp);
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa[x] || to == son[x]) continue;
            dfs(to,to);
        }
    }
    void up(int o)
    {
        w(o) = (w(o<<1) + w(o<<1|1)) % p;
        sum(o) = (sum(o<<1) + sum(o<<1|1)) % p;
    }
    void cover(int o,int val)
    {
        tag(o) += val;
        sum(o) = (sum(o) + w(o) * val % p) % p;
    }
    void down(int o)
    {
        if(tag(o))
        {
            cover(o<<1,tag(o));
            cover(o<<1|1,tag(o));
            tag(o) = 0;
        }
    } 
    void build(int o,int L,int R)
    {
        l(o) = L, r(o) = R;
        if(L == R)
        {
        	int x = id[L];
            w(o) = len[x]-len[link[x]];
            return;
        }
        int mid = (L+R)>>1;
        build(o<<1,L,mid);
        build(o<<1|1,mid+1,R);
        up(o);
    }
    void chenge(int o,int L,int R)
    {
        if(L <= l(o) && R >= r(o))
        {
            cover(o,1);
            return;
        }
        down(o);
        int mid = (l(o)+r(o))>>1;
        if(L <= mid) chenge(o<<1,L,R);
        if(R > mid) chenge(o<<1|1,L,R);
        up(o);
    }
    int query(int o,int L,int R)
    {
        int res = 0;
        if(L <= l(o) && R >= r(o)) return sum(o);
        down(o);
        int mid = (l(o) + r(o))>>1;
        if(L <= mid) res = (res + query(o<<1,L,R)) % p;
        if(R > mid) res = (res + query(o<<1|1,L,R)) % p;
        return res;
    }
    void modify(int x,int y)
    {
        while(top[x] != top[y])
        {
            if(dep[top[x]] < dep[top[y]]) swap(x,y);
            chenge(1,dfn[top[x]],dfn[x]);
            x = fa[top[x]];
        }
        if(dep[x] >= dep[y]) swap(x,y);
        chenge(1,dfn[x],dfn[y]);
    }
    int get_ans(int x,int y)
    {
        int res = 0;
        while(top[x] != top[y])
        {
            if(dep[top[x]] < dep[top[y]]) swap(x,y);
            res = (res + query(1,dfn[top[x]],dfn[x])) % p;
            x = fa[top[x]];
        }
        if(dep[x] >= dep[y]) swap(x,y);
        res = (res + query(1,dfn[x],dfn[y]));
        return res;
    }
    int main()
    {
        freopen("string.in","r",stdin);
        freopen("string.out","w",stdout);
        scanf("%d%s",&n,s+1);
        last = cnt = 1; 
        for(int i = 1; i <= n; i++) Extend(s[i]-'a'), st[i] = last;
        for(int i = 2; i <= cnt; i++) add(link[i],i), add(i,link[i]);
        get_tree(1); dfs(1,1); build(1,1,cnt);
        for(int i = 1; i <= n; i++)
        {
            ans[i] = get_ans(1,st[i]);
            modify(1,st[i]);
        }
        for(int i = 1; i <= n; i++) ans[i] = (ans[i] + ans[i-1]) % p;
        for(int i = 1; i <= n; i++)
        {
            ans[i] = (ans[i] + ans[i-1]) % p;
            printf("%d
    ",ans[i]);
        }
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2 花钟计数

    题意描述

    原题

    花卉时钟一直矗立在镜湖边。尽管不能留住时间,但它提醒人们时间的过去,使人们想起过去美好的时光。

    在花卉时钟的边缘一共有 (2n) 朵花,顺时针依次编号为 (1)(2n),每一朵花的颜色为 (n) 种可能的颜色中的一种。对于每种颜色,恰有两朵花是这种颜色的,这两朵花的距离要么不大于 (2),要么等于 (n)。另外,如果花 (u) 和花 (v) 有相同的颜色,那么 (u) 对面的那一朵花和 (v) 对面的那一朵花的颜色也应该相同——对称是美的!

    形式化的说,两朵花之间的距离是 (1) 加上夹在以这两朵花为端点的劣弧(或者半圆)之间的花的数目。下面是当 (n=6) 时一种可能的安排颜色的方式:

    img

    我们定义一种安排方式的美感为被所有相对(距离为 (n))且颜色相同的花分开的连续段的长度的乘积。换句话说,为了计算美感,我们先在环上移除那些与相对的花颜色相同的花。然后,它的美感就是剩下的连续段长度的乘积。注意到本题中包括了长度为 (0) 的那些连续段。在某种安排方式中,如果没有任何一朵花使得与它相对的花的颜色与它相同,那么这种安排方式的美感为 00。比如说,上图的安排方式的美感等于 (1 imes 3 imes 1 imes 3=9)——这些连续段为 ({2},{4,5,6},{8},{10,11,12})

    尽管保持了这些约束条件,但是可能的安排方式会有多种,你需要计算出所有可能的安排方式的美感之和,对 (998244353) 取模。两个安排方式被视为不同的,当且仅当存在一个二元组 ((u,v)(1leq u,vleq 2n)) 使得在其中一种安排方式中花 (u) 和花 (v) 颜色相同而在另一种安排方式中颜色不同。

    数据范围:(nleq 50000)

    solution

    计数套分治 (NTT)

    自己的计数水平大概是小学生水平,看了半天题解都没看懂 /cy。

    T3 平面标记

    题意描述

    平面上有 (n) 个点,第 (i) 个点的坐标为 ((x_i,y_i))

    你会依次收到 (m) 个操作,第 (i) 个操作形如 (k_i,a_i),表示你需要标记所有满足 (y_j>{k_iover x_j^{a_i}}) 的点 (j)

    在执行完所有操作后,你想知道每个点在哪次操作中第一次被标记。

    数据范围:(1leq nleq m,0<x,y<1000,0<k<10^6,0<a<10)

    solution

    线段树分治+半平面交或者整体二分套半平面交。

    考虑先转化一下条件,即:

    (y_j>{k_iover x_j^{a_i}} Leftrightarrow y_j imes x_j^{a_i} > k_i)

    对于两个大数比较大小,我们有一个很常用的技巧就是分别对其去 (ln),则有:

    (ln y_j + a_i ln x_j > ln k_i)

    这显然是一个半平面交的形式。将询问的点 ((x,y)) 变为 ((ln x,ln y)) ,并把所有的限制条件取反,二分一个答案 (t),

    然后问题就转化为判断前 (t) 条直线所组成的半平面是否包含 ((ln x,ln y)) 这个点。

    注意到给定的半平面一定是一条斜率小于 0 的直线下方的区域,因此组成的半平面交一定是一个上凸壳下方的区域,可以直接在凸壳上二分得到横坐标范围包含询问点的线段来判断。

    具体地,我们对所有半平面维护一棵线段树,在线段树上的每个结点记录该结点对应区间的上凸壳,询问一个点时就直接在线段树上二分。

    时间复杂度:(O(nlogn^2))

    代码

    //太难写了,咕咕咕
    
  • 相关阅读:
    Angular 项目打包之后,部署到服务器,刷新访问404解决方法
    C# yield return; yield break;
    C#获取枚举描述
    21、uwp UI自动化测试(WinAppDriver)
    20、uwp打包失败(All app package manifests in a bundle must declare the same values under the XPath *[local-name()='Package']/*[local-name()='Dependencies'])
    19、UWP 新特性(Creator Update)
    18、利用 Windows Device Portal 获取用户闪退 dump
    17、uwp 打包失败记录
    爱奇艺招聘uwp开发
    16、C++获取磁盘空间的方法
  • 原文地址:https://www.cnblogs.com/genshy/p/14599046.html
Copyright © 2020-2023  润新知