首页 > 其他分享 >NOI 2017 蚯蚓排队 题解

NOI 2017 蚯蚓排队 题解

时间:2024-01-29 17:45:13浏览次数:41  
标签:数字串 NOI int 题解 unsigned long 或合点 2017 断点

Meaning

给定一些数字,对它们进行首尾相接和断开两种操作。对于每次询问,求对于每个数字,其后长度一定的数字串在给定数字串中出现的次数,并给出这些次数之积。

Soultion

对于每次首尾相接或断开的操作,如果直接对断点或合点两侧的整个数字串进行操作,时间复杂度不可接受。由于每次查询只有向后 \(k\) 数字串会产生贡献,所以假设 \(k\) 一定,则只需要对断点或合点左侧 \(k-1\) 个数字进行操作。这道题的向后 \(k\) 数字串的长度极为有限,所以当断点或合点左侧最靠近断点或合点的数字是需要修改的数字的第 \(i\) 个后继时,我们即使在每次操作时对每一个需要修改的数字修改它的向后 \(i+2\) 到 \(50\)( \(k\) 的上界)数字串,也不会产生过大的时间开销。
在具体的操作过程中,我们可以取出以断点或合点为圆心,以 \(50\) 为半径的圆内的点,对断点或合点左侧的数字枚举向后数字串的长度记录这个数字串并累加它的出现次数即可。
为了方便取出这些圆上的点,我们可以使用链表对每个数字的前驱与后继进行记录,在对数字串进行操作的时候一并操作。

只需要取以断点或合点为圆心,以 $50$ 为半径的圆内的点的原因

当左侧一个数字的第 \(i-1\) 个后继在断点或合点右侧时这个数字的向后 \(i\) 数字串才会改变,所以左侧只需取 \(49\) 个点。
而即使是断点或合点左侧最靠近断点或合点的数字,也只需要最多 \(49\) 个右侧的数为它做出贡献,所以只需要取圆内的点,而不需要圆上与圆外的点。

由于在查询操作中需要求出数字串的子串,我们将所有对数字串的操作变为字符串操作。为了避免使用一些常数较大的 STL,考虑用数字代表每个字符串,将每个向后数字串哈希起来代表这个数字串,并使用哈希表记录每个数字串的出现次数,在查询操作时查询每个长度为 \(k\) 的子串的哈希值与对应出现次数并进行求积,即可得到答案。

Code

#include<bits/stdc++.h>
using namespace std;
const unsigned long long mdr=998244353,pp=33557999;
unsigned long long n,m,a[200010],pre[200010],nxt[200010],t[200010];
inline int numhash(unsigned long long x){
    return x%pp;
}
struct node{
    unsigned long long val;
    unsigned long long hashv;
    int next;
};
struct table{
    node data[35000000];
    int cnt,head[35000000];
    inline unsigned long long& operator [] (unsigned long long x){
        int temh=numhash(x);
        for(register int i=head[temh];i;i=data[i].next){
            if(data[i].val==x)
            return data[i].hashv;
        }
        ++cnt;
        data[cnt]={x,0,head[temh]};
        head[temh]=cnt;
        return data[cnt].hashv;
    }
}mp,str;
int opt,o,p,pos;
string s;
int main(){
    scanf("%llu%llu",&n,&m);
    t[0]=1;
    for(register int i=1;i<=n;++i){
        t[i]=t[i-1]*11;
    }
    for(register int i=1;i<=n;++i){
        scanf("%llu",&a[i]);
        ++mp[a[i]];
    }
    for(;m>0;--m){
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&o,&p);
            int lft=49,rgt=50;
            unsigned long long tem[150],hs[150];
            for(register int i=o;i&&lft;i=pre[i],--lft){
                tem[lft]=a[i];
            }
            ++lft;
            for(register int i=p;i&&rgt<99;i=nxt[i],++rgt){
                tem[rgt]=a[i];
            }
            --rgt;
            for(register int i=lft;i<=rgt;++i){
                hs[i]=hs[i-1]*11+tem[i];
            }
            for(register int i=lft;i<50;++i){
                for(register int len=51-i;len<=min(50,rgt-i+1);++len){
                    int j=i+len-1;
                    ++mp[hs[j]-hs[i-1]*t[len]];
                }
            }
            nxt[o]=p;
            pre[p]=o;
        }else if(opt==2){
            scanf("%d",&o);
            p=nxt[o];
            int lft=49,rgt=50;
            unsigned long long hs[150],tem[150];
            for(register int i=o;i&&lft;i=pre[i],--lft){
                tem[lft]=a[i];
            }
            ++lft;
            for(register int i=p;i&&rgt<99;i=nxt[i],++rgt){
                tem[rgt]=a[i];
            }
            --rgt;
            for(register int i=lft;i<=rgt;++i){
                hs[i]=hs[i-1]*11+tem[i];
            }
            for(register int i=lft;i<50;++i){
                for(int len=51-i;len<=min(50,rgt-i+1);++len){
                    int j=i+len-1;
                    --mp[hs[j]-hs[i-1]*t[len]];
                }
            }
            nxt[o]=0;
            pre[p]=0;
        }else{
            cin>>s;
            scanf("%d",&o);
            int tem=s.length();
            for(int i=0;i<tem;++i){
                str[i+1]=str[i]*11+s[i]-'0';
            }
            unsigned long long ans=mp[str[o]]%mdr;
            for(int i=o+1;i<=tem;++i){
                ans=ans*mp[str[i]-str[i-o]*t[o]]%mdr;
            }
            printf("%llu\n",ans);
        }
    }
    return 0;
}

标签:数字串,NOI,int,题解,unsigned,long,或合点,2017,断点
From: https://www.cnblogs.com/blog21012004/p/17995000

相关文章

  • CF1764H Doremy's Paint 2 题解
    题目链接:CF或者洛谷高分题,感觉挺有意思的题,值得一提的是这个题的\(1\)和\(3\)版本却是两个基础题。一开始以为跟这道差不多:P8512[YnoiEasyRound2021]TEST_152题解。后面重新读了一下发现一个有趣的点:也就是是说操作的\(val\)并不太好搞了,如果\(val\)确定就基......
  • P5400 [CTS2019] 随机立方体 题解
    题目链接点击打开链接题目解法参考cmd的博客好复杂的推式子题,而且三维的对我来说好难想象/ll首先二项式反演,把恰好\(k\)个变成求至少\(i\)个的方案数令极大格子有至少\(i\)个的方案数为\(f_i\),\(R=\min\{n,m,k\}\)特判掉\(k>R\)答案为\(0\)根据二项式反演,答案......
  • P1438 无聊的数列 题解
    背景看到题解都是差分,竟然还有建两颗线段树和二阶差分的大佬。我感到不理解,很不理解。题目正解本题正解很明显就是:线段树是的,你没有看错,就只有线段树。很显然我们直接按照线段树板题写就可以了,维护题目需要维护的,注意到只有单点查询,所以我们根本不需要维护区间和,对于区间来......
  • 洛谷P10114题解
    题意简述给定一个长度为\(n\)的序列,求\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{((d_i\oplusd_j)+(d_i\otimesd_j))}\)。思维路径观察数据范围可以发现\(n\le2\times10^6\)而\(\sum\limitsd_i\le5\times10^7\),这说明\(d\)数组中的重复值较多,直接枚举数值可......
  • 洛谷题解-[ABC325E] Our clients, please wait a moment
    https://www.luogu.com.cn/problem/AT_abc325_e题目描述ある国には都市がNNN個あります。あなたは、都市111にある営業所から000個以上の都市を経由して都市NNNにある訪問先へ移動しようとしています。移動手段は社用車と電車の222種類があります。都市......
  • 洛谷题单指南-排序-P1093 [NOIP2007 普及组] 奖学金
    原题链接:https://www.luogu.com.cn/problem/P1093题意解读:本题考察排序,根据题意,先按总分从大到小排,再按语文从大到小排,以上都相同则按学号从小到大排。100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=305;structstudent{intid;intyuw......
  • 洛谷题单指南-排序-P1059 [NOIP2006 普及组] 明明的随机数
    原题链接:https://www.luogu.com.cn/problem/P1059题意解读:此题主要做两件事:排序+去重,用计数排序即可解决,直接给出代码。100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=1005;inta[N];intn;intmain(){cin>>n;intx;intcnt......
  • 洛谷P10115题解
    题意简述给定一个括号序列,求整个序列的美丽值最大。思维路径见到序列和权值,很容易想到需要用DP。我们定义\(f[i][j]\)表示第\(1\)到\(i\)个括号产生的美丽值最大值,其中\(j=0\)表示第\(i\)个括号本身不参与美丽值贡献,\(j=1\)表示第\(i\)个括号本身参与美丽值贡献......
  • B3929 [GESP202312 五级] 小杨的幸运数 题解
    因为一些众所周知的原因,不放代码。分享一种考场思路:\(a\le10^7\),顺序查找肯定会废,所以可以用一种类似埃氏筛的方法将所有满足条件的数标记一下,并将其加入一个答案数组\(a\)当中。然后每次查询只需要用lower_bound函数二分查找一下,假如找到了第\(i\)个:\(a_i=x\),直接......
  • P7324 [WC2021] 表达式求值 题解
    题目链接点击打开链接题目解法不错的题首先建出表达式树说实话我一开始不知道怎么建,但看了代码之后就懂了这里简单说一下:假如要对区间\([l,r]\)建树,分\(E_r=)\)和\(E_r\neq)\)的情况\(E_r=)\),找到匹配的左括号,递归下去建树\(E_r\neq)\),\(r\)可以作为单独的一个......