题解 洛谷 P4696 【[CEOI2011]Matching】

题意即为在序列中找出给定排列能匹配相同的位置,这里的匹配相同指的是相对大小关系相同,即离散化后相同。

因为是相对大小关系相同,所以只需考虑每个数所在的排名。可以对给定排列处理出 \(num_i\),为位置 \(i\) 之前小于位置 \(i\) 对应的数的个数,发现对于序列,\(num_i\) 相同,相对大小关系就相同。

那么就只用考虑序列和给定排列 \(num_i\) 的匹配,这个可以通过 \(KMP\) 来实现,用树状数组来维护,匹配成功时在树状数组上加入贡献,失配时在树状数组上删去贡献。

\(code:\)

#include<bits/stdc++.h>
#define maxn 1000010
#define lowbit(x) (x&(-x))
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,pos,cnt;
int p[maxn],a[maxn],s[maxn],t[maxn],num[maxn],nxt[maxn],ans[maxn];
void update(int x,int v)
{
    while(x<=m) t[x]+=v,x+=lowbit(x);
}
int query(int x)
{
    int v=0;
    while(x) v+=t[x],x-=lowbit(x);
    return v;
}
void clear()
{
    pos=0;
    for(int i=1;i<=m;++i) t[i]=0;
}
int main()
{
    read(n),read(m),num[n+1]=-1;
    for(int i=1;i<=n;++i)
    {
        int x;
        read(x),p[x]=i;
    }
    for(int i=1;i<=m;++i) read(a[i]),s[i]=a[i];
    sort(s+1,s+m+1);
    for(int i=1;i<=m;++i) a[i]=lower_bound(s+1,s+m+1,a[i])-s;
    for(int i=1;i<=n;++i) num[i]=query(p[i]),update(p[i],1);
    clear();
    for(int i=2;i<=n;++i)
    {
        while(pos&&query(p[i])!=num[pos+1])
        {
            for(int j=i-pos;j<i-nxt[pos];++j) update(p[j],-1);
            pos=nxt[pos];
        }
        if(query(p[i])==num[pos+1]) update(p[i],1),pos++;
        nxt[i]=pos;
    }
    clear();
    for(int i=1;i<=m;++i)
    {
        while(pos&&query(a[i])!=num[pos+1])
        {
            for(int j=i-pos;j<i-nxt[pos];++j) update(a[j],-1);
            pos=nxt[pos];
        }
        if(query(a[i])==num[pos+1]) update(a[i],1),pos++;
        if(pos==n) ans[++cnt]=i-n+1;
    }
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;++i) printf("%d ",ans[i]);
    return 0;
}
posted @ 2020-08-04 07:57  lhm_liu  阅读(159)  评论(0编辑  收藏  举报