花神的嘲讽计划 莫队做法

前言

  • loj
  • 其实这题是主席树题单的但是没要求在线就用莫队屮一发
  • 张技问msjing这题有啥思路没,msjing给他胡了一个莫队+bitset的做法,然后撂了两天来做了
  • 刷 Ynoi 大史的好处体现了
  • \(Waring!\) 由于此题的提交处过少,不保证代码正确性
  • msjing瞎搞导致内存爆炸了

solve

  • 要求一个区间内序列有没有在原序列出现过,我们想到这与一个区间内一个元素是否在原序列出现过很相似,考虑向这个方向转化
  • 我们发现要求这个区间内的序列必须完全相同,相当于字符串匹配,可以做个 \(KMP\) 的 \(O(n^2)\) 的暴力,将询问的序列变成字符串,并与原串做匹配
  • 我们顺着这个向下想,由于询问定长,我们是不是可以对原串按 \(k\) 长拆分,这样很好比
  • 可以的,复杂度大概是 \(O(n)\) 的,带一个巨大无比接近 \(\log\) 的常数
  • 一个串和一个串显然不好做,我们将所有串做哈希映射,将所有串变成了哈希值,这样就好比了
  • msjing是个兰丹不想写双哈希用的 unordered_map,可能会被卡
  • 那问题变成了在一个区间里求一个数是否在一个序列出现过,这个就是莫队的基本操作了,出没出现过可以用 \(bool\) 数组维护,\(bitset\) 也行(实测好像 \(bitset\) 更慢但是空间更优?)
记录
  • 数组

2026-08-19 09-29-32屏幕截图

  • \(bitset\)

2026-08-19 09-30-07屏幕截图

  • 但是你发现这个串有长度,所以我们将哈希值全放在右端点上(左端点好像也行)
点击查看代码
for (int i=1;i<=n-k+1;i++)
{
    string s;
    for (int j=i;j<=i+k-1;j++) s+=b[j]+'0';
    if (!mp[s]) mp[s]=++tot;
    // cerr << s << endl;
    a[i+k-1]=mp[s];
}
  • 但是你又发现可能在莫队时有不合法决策被加入,所以我们特殊处理一下左端点,这个看代码好理解
  • 剩下的就是莫队基础操作了,时间复杂度 \(O(n \sqrt n)\)
点击查看代码
#include <bits/stdc++.h>
// #define int long long
using namespace std;
constexpr int maxn=2e5+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m,k;
int a[maxn],b[maxn];
unordered_map<string,int> mp;
int tot;
int L[maxn],R[maxn],pos[maxn],cnt;
void init()
{
    cnt=sqrt(n);
    for (int i=1;i<=cnt;i++)
        L[i]=R[i-1]+1,R[i]=cnt*i;
    if (R[cnt]<n)
        cnt++,L[cnt]=R[cnt-1]+1,R[cnt]=n;
    for (int i=1;i<=cnt;i++)
        for (int j=L[i];j<=R[i];j++)
            pos[j]=i;
}
struct _
{
    int l,r;
    int strid;
    int id;
}q[maxn];
int tt;
int col[maxn];
int bt[maxn];
void add(int x)
{
    if (!col[x]) bt[x]=1;
    col[x]++;
}
void del(int x)
{
    col[x]--;
    if (!col[x]) bt[x]=0;
}
int ans[maxn];
signed main()
{
    n=read(),m=read(),k=read();
    init();
    // for (int i=1;i<=cnt;i++)
    //     cerr << L[i] << " " << R[i] << endl;
    // for (int i=1;i<=n;i++)
    //     cerr << pos[i] << " ";
    // cerr << endl;
    for (int i=1;i<=n;i++) b[i]=read();
    for (int i=1;i<=n-k+1;i++)
    {
        string s;
        for (int j=i;j<=i+k-1;j++) s+=b[j]+'0';
        if (!mp[s]) mp[s]=++tot;
        // cerr << s << endl;
        a[i+k-1]=mp[s];
    }
    // for (int i=1;i<=n;i++)
    //     cerr << a[i] << " ";
    // cerr << endl;
    for (int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        string g;
        for (int j=1;j<=k;j++)
        {
            int x=read();
            g+=x+'0';
        }
        if (!mp[g]) mp[g]=++tot;
        // cerr << mp[g] << endl;
        if (l+k-1>r)
        {
            ans[i]=1;
            continue;
        }
        q[++tt]={l+k-1,r,mp[g],i};
        /*特殊处理左端点*/
    }
    sort(q+1,q+1+tt,[](_ a,_ b)
    {return pos[a.l] == pos[b.l] ? (a.l&1) == 1 ? 
    a.r<b.r : a.r>b.r : a.l<b.l;});
    int l=1,r=0;
    for (int i=1;i<=tt;i++)
    {
        // cerr << q[i].id << " : ";
        // cerr << q[i].l << " " << q[i].r << endl;
        while (l>q[i].l) add(a[--l]);
        while (r<q[i].r) add(a[++r]);
        while (l<q[i].l) del(a[l++]);
        while (r>q[i].r) del(a[r--]);
        // cerr << l << " " << r << endl;
        // cerr << q[i].strid << endl;
        // for (int i=1;i<=n;i++)
        //     cerr << bt[i] << " ";
        // cerr << endl;
        (!bt[q[i].strid]) ? ans[q[i].id]=1 : ans[q[i].id]=0;
    }
    for (int i=1;i<=m;i++)
        ans[i] ? puts("Yes") : puts("No");
    return 0;
}
posted @ 2026-08-19 09:34  msjing  阅读(11)  评论(0)    收藏  举报