首页 > 其他分享 >P9482 [NOI2023] 字符串 题解

P9482 [NOI2023] 字符串 题解

时间:2024-08-25 16:37:20浏览次数:4  
标签:le P9482 题解 int NOI2023 maxn 2l 2n rk

题目描述

\(T\) 组数据,给定长为 \(n\) 的字符串 \(s\) ,\(q\) 次询问,给定 \(i,r\) ,求有多少个 \(l\) 满足:

  • \(1\le l\le r\) 。
  • \(s[i:i+l-1]\) 字典序小于 \(R(s[i+l:i+2l-1])\) 。

数据范围

  • \(1\le T\le 5,1\le n,q\le 10^5,1\le i+2r-1\le n\) 。

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{512MB}\) 。

分析

先考虑如何比较这两个字符串的字典序,容易发现这是后缀数组的拿手好戏。具体的,对 \(s+R(s)\) 建立后缀数组,产生贡献当且仅当下面两个条件同时成立:

  • \(rk_i\lt rk_{2n+2-i-2l}\) 。
  • \(\texttt{lcp}(i,2n+2-i-2l)\lt l\) 。

先只考虑第一个条件并进行计数,算上 \(1\le l\le r\) 的限制,本质上是一个二维数点问题。

将 \((j,rk_j)\) 看成平面上的点,我们需要求出 \(2n+2-i-2r\le x\le 2n-i,y\gt rk_i\) 中的点数,询问离线后按 \(rk\) 倒序枚举 \(i\) ,注意需要对奇偶分别开树状数组。

再考虑第二个条件,我们多算了满足以下条件的 \(l\) ,需要减去:

  • \(1\le l\le r\) 。
  • \(rk_i\lt rk_{2n+2-i-2l}\) 。
  • \(\texttt{lcp}(i,2n+2-i-2l)\ge l\),即 \(s[i:i+l-1]=R(s[i+l,i+2l-1])\) 。

对于第三个条件,先跑 \(\texttt{manacher}\) 算法,枚举回文中心 \(j=i+l-1\),则其等价于 \(l\le o_j\) 。这里 \(o_i\) 表示以第 \(i\) 和第 \(i+1\) 个字符中间为中心,最长非 # 回文半径。

对于第二个条件,由于第 \(i\) 个后缀和第 \(2n+2-i-2l\) 个后缀的前 \(l\) 个字符相同,根据字典序的比较规则,我们直接忽略掉这 \(l\) 个字符,所以其等价于 \(rk_{j+1}\lt rk_{2n+1-j}\) 。

现在这个条件只与 \(j\) 有关,它会贡献到满足以下条件的询问:

  • \(1\le j-i+1\le\min(r,o_j)\) 。

这个条件等价于:

  • \(i\le j\le i+r-1\) 。
  • \(j-o_j+1\le i\) 。

再来一遍二维数点即可。

时间复杂度 \(\mathcal O((n+q)\log n)\) 。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int m,n,q,t;
int c[maxn],x[maxn],y[maxn];
int rk[maxn],sa[maxn];
int p[maxn],res[maxn];
char s[maxn];
struct node
{
    int x,id,op;
};
vector<node> v1[maxn],v2[maxn];
void get_sa()
{
    for(int i=1;i<=n;i++) s[2*n+1-i]=s[i];
    memset(c,0,sizeof(c));
    memset(y,0,sizeof(y));
    int m=122,n=2*::n;
    for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
    for(int i=1;i<=m;i++) c[i]+=c[i-1];
    for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int num=0;
        for(int i=n-k+1;i<=n;i++) y[++num]=i;
        for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) c[x[i]]++;
        for(int i=1;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i];
        for(int i=1;i<=n;i++) y[i]=x[i],x[i]=0;
        x[sa[1]]=num=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?num:++num;
        if(num==n) break;
        m=num;
    }
    for(int i=1;i<=n;i++) rk[sa[i]]=i;
}
void manacher()
{
    static char t[maxn];
    int m=2*n+1;
    for(int i=m;i>=1;i--) p[i]=0,t[i]=i&1?'#':s[i>>1];
    for(int i=1,pos=0,mxr=0;i<=m;i++)
    {
        if(i<=mxr) p[i]=min(p[2*pos-i],mxr-i+1);
        while(i+p[i]<=m&&t[i-p[i]]==t[i+p[i]]) p[i]++;
        if(i+p[i]-1>mxr) pos=i,mxr=i+p[i]-1;
    }
}
struct bit
{
    int n,op,c[maxn];
    void clean(int _n,int _op)
    {
        n=_n,op=_op,memset(c,0,4*n);
    }
    void add(int x,int v)
    {
        if(!op) while(x) c[x]+=v,x-=x&(-x);
        else while(x<=n) c[x]+=v,x+=x&(-x);
    }
    int query(int x)
    {
        int res=0;
        if(!op) while(x<=n) res+=c[x],x+=x&(-x);
        else while(x) res+=c[x],x-=x&(-x);
        return res;
    }
}b[3];
int main()
{
    for(scanf("%*d%d",&t);t--;)
    {
        scanf("%d%d%s",&n,&q,s+1),m=2*n;
        get_sa(),manacher();
        b[0].clean(m,0),b[1].clean(m,0),b[2].clean(m,1);
        for(int i=1;i<=m;i++) v1[i].clear(),v2[i].clear();
        for(int j=1,i=0,r=0;j<=q;j++)
        {
            scanf("%d%d",&i,&r),res[j]=0;
            v1[2*n-i].push_back({rk[i]+1,j,1});
            v1[2*n-i-2*r].push_back({rk[i]+1,j,-1});
            v2[i+r-1].push_back({i,j,1});
            v2[i-1].push_back({i,j,-1});
        }
        for(int i=1;i<=m;i++)
        {
            b[i&1].add(rk[i],1);
            for(auto u:v1[i]) res[u.id]+=u.op*b[i&1].query(u.x);
        }
        for(int i=1;i<=n;i++)
        {
            if(rk[i+1]<rk[2*n+1-i]) b[2].add(i-(p[2*i+1]>>1)+1,1);
            for(auto u:v2[i]) res[u.id]-=u.op*b[2].query(u.x);
        }
        for(int i=1;i<=q;i++) printf("%d\n",res[i]);
    }
    return 0;
}

总结

  • 数点条件不好刻画时可以利用字典序的性质进行转化,笔者考场上就差这一步没想到最终 \(72pts\) 含恨回家。

标签:le,P9482,题解,int,NOI2023,maxn,2l,2n,rk
From: https://www.cnblogs.com/peiwenjun/p/18379102

相关文章

  • Triple Attack 题解
    直接暴力显然不可行。我们容易发现,变量\(T\)的增量以\(3\)为循环,一次循环会造成\(5\)的贡献,所以我们容易想到对每个\(a_i\)直接对\(5\)计算倍数和取余,然后对于余数分类讨论去增加,然后对于倍数部分统一增加即可。有些细节。Code#include<bits/stdc++.h>//#include......
  • Minimum Steiner Tree 题解
    原题,详见P10723。几乎相同,我们只需要以一个需要选择的点为根,遍历子树看看有没有出现需要选择的点,然后直接去删除即可,可以看我的博客。但是我们也可以换一种做法,用类似拓扑排序的算法。先找到所有只连一条边且没有被选择的点,然后放进队列,接着依次取出队头遍历与它相连的点,同时记......
  • k8s中coredns访问连接拒绝问题解决
    问题现象1、节点访问coredns连接拒绝2、内部pod无法正常进行解析问题解决思路检查CoreDNSPod状态是否正常[root@k8s-master01~]#kubectlgetpods-nkube-system-lk8s-app=kube-dnsNAMEREADYSTATUSRESTARTSAGEcoredns-7b8d6fc5......
  • CSP-J 2023 初赛试题解析(第三部分:完善程序(1-2))
    第一题补全后完整代码:#include<iostream>#include<vector>usingnamespacestd;intfind_missing(vector<int>&nums){intleft=0,right=nums.size()-1;while(left<right){intmid=left+(right-left)/2;if(nums[mi......
  • 洛谷SCP 2024 第一轮(初赛 J 组)模拟题解析(第三部分:完善程序(1-2))
    完善程序一(补全)#include<bits/stdc++.h>usingnamespacestd;constintMAXN=100000;intn;intvis[MAXN],a[MAXN];vector<int>ans;intcheck(intk){intx=n,top=0;for(inti=0;i<=k;i++)vis[i]=0;while(x......
  • 题解:P10358 [PA2024] Obrazy
    题解:P10358[PA2024]Obrazy题目传送门即当最小的画框都不可能覆盖整个矩形墙面时,输出−1。[PA2024]Obrazy题目背景PA20243C题目描述题目译自PA2024Runda3Obrazy,感谢Macaronlin提供翻译给定尺寸为$h\timesw$的矩形墙面,以及$n$种尺寸的正方形画框,每种尺寸......
  • SP10502 VIDEO - Video game combos 题解
    题目传送门前置知识AC自动机解法多模式串匹配考虑AC自动机。令\(f_{i,j}\)表示前\(i\)个字符,当前运行到AC自动机的状态\(j\)时的最大得分。状态转移方程为\(f_{i,k}=\max\limits_{k\inSon(j)}\{f_{i-1,j}+sum_{k}\}\),其中\(sum_{k}\)表示fail树上以\(k......
  • 历年CSP-J初赛真题解析 | 2015年CSP-J初赛阅读程序(23-26)
    学习C++从娃娃抓起!记录下CSP-J备考学习过程中的题目,记录每一个瞬间。附上汇总贴:历年CSP-J初赛真题解析|汇总_热爱编程的通信人的博客-CSDN博客#include<iostream>usingnamespacestd;intmain(){inta,b,c;a=1;b=2;c=3;if(a>b){......
  • abc368 题解
    切了ABCDF,G赛后1min切了(恼比赛链接:https://atcoder.jp/contests/abc368A-Cut题意:给定一个长度为\(n\)的序列,先输出后\(k\)个数,在输出前\(n-k\)个数。思路:按题意模拟即可。代码:https://atcoder.jp/contests/abc368/submissions/57030066B-Decrease2maxel......
  • CF241B Friends 题解
    是异或粽子的超级加强版,但是本题因为\(m\)很大,不能套用那一题的解法。换一种思路:考虑把\(a_i\)从高位到低位插入0-1Trie之后,二分第\(m\)大,通过第\(m\)大求答案。对于二分的一个值\(x\),枚举每个位置\(i\),在0-1Trie上找与\(a_i\)异或值大于等于\(x\)的值的......