[SCOI2012]喵星球上的点名

题目描述

a180285 幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。

假设课堂上有 N 个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M 个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。

然而,由于喵星人的字码过于古怪,以至于不能用 ASCII 码来表示。为了方便描述,a180285 决定用数串来表示喵星人的名字。

现在你能帮助 a180285 统计每次点名的时候有多少喵星人答到,以及 M 次点名结束后每个喵星人答到多少次吗?

输入输出格式

输入格式:

现在定义喵星球上的字符串给定方法:

先给出一个正整数 L ,表示字符串的长度,接下来L个整数表示字符串的每个字符。

输入的第一行是两个整数 N 和 M。

接下来有 N 行, 每行包含第 i 个喵星人的姓和名两个串。 姓和名都是标准的喵星球上的字符串。

接下来有 M 行,每行包含一个喵星球上的字符串,表示老师点名的串。

输出格式:

对于每个老师点名的串输出有多少个喵星人应该答到。

然后在最后一行输出每个喵星人被点到多少次。

 ~~~~~~~~~~~~~~~~~~

某谷恶意加强数据。

首先吐槽卡trie图。字符集太大了,trie图会T得很厉害,建个图就超时了。

别人用map,然而我不喜欢迭代器,就用了treap维护儿子。

代码:

#include<ctime>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define N 200050
#define M 200050
inline int rd()
{
    int f=1,c=0;char ch = getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=(c<<3)+(c<<1)+ch-'0';ch=getchar();}
    return f*c;
}
int n,m,tot;
int ls[M<<1],rs[M<<1],rnd[M<<1],w[M<<1];
struct Treap
{
    int rt;
    void lturn(int &x)
    {
        int y = rs[x];
        rs[x]=ls[y],ls[y]=x;
        x=y;
    }
    void rturn(int &x)
    {
        int y = ls[x];
        ls[x]=rs[y],rs[y]=x;
        x=y;
    }
    int query(int k,int x)
    {
        if(w[k]==x)return k;
        if(!k)return 0;
        return query(w[k]<x?rs[k]:ls[k],x);
    }
    void insert(int &k,int x)
    {
        if(!k)
        {
            k=++tot;
            rnd[k]=rand();
            w[k]=x;
            return ;
        }else if(w[k]<x)
        {
            insert(rs[k],x);
            if(rnd[rs[k]]<rnd[k])lturn(k);
        }else
        {
            insert(ls[k],x);
            if(rnd[ls[k]]<rnd[k])rturn(k);
        }
    }
};
int lin[M],sum;
struct nam
{
    int len1,len2;
    int l1,r1,l2,r2;
}q[N];
int hed[M<<1],cntt;
struct Trie
{
    Treap ch;
//    vector<int>vv;
    int f,ct;
}tr[M<<1];
struct EG
{
    int to,nxt;
}e[M];
void ae(int f,int t)
{
    e[++cntt].to = t;
    e[cntt].nxt = hed[f];
    hed[f] = cntt;
}
bool vis1[M],vis2[M];
void init()
{
    for(int i=1;i<=tot;i++)
        vis1[i]=0;
    for(int i=1;i<=n;i++)
        vis2[i]=0;
}
void acmach()
{
    queue<int>q,c;
    q.push(0);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        if(!tr[u].ch.rt)continue;
        c.push(tr[u].ch.rt);
        while(!c.empty())
        {
            int v = c.front();
            c.pop();
            if(ls[v])c.push(ls[v]);
            if(rs[v])c.push(rs[v]);
            int to = tr[u].f;
            while(to&&!tr[to].ch.query(tr[to].ch.rt,w[v]))to=tr[to].f;
            int hhh=tr[to].ch.query(tr[to].ch.rt,w[v]);
            if(hhh&&u)to=hhh;
            tr[v].f = to;
            q.push(v);
        }
    }
}
int as1[N],as2[M];
int deal(int u)
{
    int ret = 0;
    while(!vis1[u])
    {
        for(int j=hed[u];j;j=e[j].nxt)
        {
            int i = e[j].to;
            vis2[i]=1;
            as2[i]++;
        }
        ret+=tr[u].ct;
        vis1[u]=1;
        u = tr[u].f;
    }
    return ret;
}
int main()
{
    srand(time(NULL));
    n=rd(),m=rd();
    int u,c,v;
    for(int i=1;i<=n;i++)
    {
        q[i].len1=rd();
        q[i].l1 = sum+1,q[i].r1 = sum+q[i].len1;
        for(int j=q[i].l1;j<=q[i].r1;j++)lin[j]=rd();
        sum+=q[i].len1;
        q[i].len2=rd();
        q[i].l2 = sum+1,q[i].r2 = sum+q[i].len2;
        for(int j=q[i].l2;j<=q[i].r2;j++)lin[j]=rd();
        sum+=q[i].len2;
    }
    for(int i=1;i<=m;i++)
    {
        c=rd();
        u=0;
        for(int j=1;j<=c;j++)
        {
            v=rd();
            if(!tr[u].ch.query(tr[u].ch.rt,v))tr[u].ch.insert(tr[u].ch.rt,v);
            u = tr[u].ch.query(tr[u].ch.rt,v);
        }
//        tr[u].vv.push_back(i);
        ae(u,i);
        tr[u].ct++;
    }
    acmach();
    vis1[0]=1;
    for(int i=1;i<=n;i++)
    {
        init();
        u=0;
        for(int j=q[i].l1;j<=q[i].r1;j++)
        {
            c = lin[j];
            while(u&&!tr[u].ch.query(tr[u].ch.rt,c))u=tr[u].f;
            int hhh = tr[u].ch.query(tr[u].ch.rt,c);
            if(hhh)u=hhh;
            as1[i]+=deal(u);
        }
        u=0;
        for(int j=q[i].l2;j<=q[i].r2;j++)
        {
            c = lin[j];
            while(u&&!tr[u].ch.query(tr[u].ch.rt,c))u=tr[u].f;
            int hhh = tr[u].ch.query(tr[u].ch.rt,c);
            if(hhh)u=hhh;
            as1[i]+=deal(u);
        }
    }
    for(int i=1;i<=m;i++)printf("%d\n",as2[i]);
    for(int i=1;i<=n;i++)printf("%d ",as1[i]);
    printf("\n");
    return 0;
}

后来才知道AC自动机并不是正解,只是当时数据水能过……

现在讲一下后缀自动机做法(貌似也不是正解

对于姓名串我们用广义后缀自动机存上,然后离线处理点名达到数。

处理方法依然为树状数组之HH的项链。

这一段复杂度为O(n logn *map的log);

对于答道次数,我们可以将姓名串放在后缀自动机上跑一遍,每到一个节点就沿着pre指针跳到第一个到过的地方。

复杂度好像O(n)?

代码:

#include<map>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 50050
#define M 100050
int n,m;
struct Point
{
    int pre,len;
    map<int,int>trs;
    vector<int>ve;
}p[2*M];
struct EG
{
    int to,nxt;
}e[2*M];
int hed[2*M],cnt;
void ae(int f,int t)
{
    e[++cnt].to = t;
    e[cnt].nxt = hed[f];
    hed[f] = cnt;
}
int tin[2*M],tout[2*M],tim,pla[2*M],v[2*M];
struct SAM
{
    int tot,las;
    SAM(){tot=las=1;}
    void res(){las=1;}
    void insert(int c,int k)
    {
        int np,nq,lp,lq;
        np=++tot;
        p[np].len = p[las].len+1;
        for(lp = las;lp&&p[lp].trs.find(c)==p[lp].trs.end();lp=p[lp].pre)
            p[lp].trs[c] = np;
        if(!lp)p[np].pre = 1;
        else
        {
            lq = p[lp].trs[c];
            if(p[lq].len==p[lp].len+1)p[np].pre = lq;
            else
            {
                nq=++tot;
                p[nq] = p[lq];
                p[nq].len = p[lp].len+1;
                p[lq].pre = p[np].pre = nq;
                while(p[lp].trs[c]==lq)
                {
                    p[lp].trs[c]=nq;
                    lp = p[lp].pre;
                }
            }
        }
        las = np;
        p[np].ve.push_back(k);
    }
    void dfs(int u)
    {
        tin[u]=++tim;pla[tim]=u;
        for(int j=hed[u];j;j=e[j].nxt)dfs(e[j].to);
        tout[u]=tim;
    }
    void build()
    {
        for(int i=2;i<=tot;i++)
            ae(p[i].pre,i);
        dfs(1);
    }
}sam;
vector<int>nam[N][2];
int ct,ans[M];
int las[N];
struct Que
{
    int l,r,id;
}que[M];
bool cmp(Que a,Que b)
{
    return a.r<b.r;
}
int f[2*M];
void up(int x,int d)
{
    if(!x)return ;
    while(x<=200000)f[x]+=d,x+=(x&(-x));
}
int down(int x)
{
    if(!x)return 0;
    int ret = 0;
    while(x)ret+=f[x],x-=(x&(-x));
    return ret;
}
int vis[2*M];
int main()
{
    scanf("%d%d",&n,&m);
    for(int c,x,i=1;i<=n;i++)
    {
        scanf("%d",&x);
        sam.res();
        while(x--)
        {
            scanf("%d",&c);
            nam[i][0].push_back(c);
            sam.insert(c,i);
        }
        scanf("%d",&x);
        sam.res();
        while(x--)
        {
            scanf("%d",&c);
            nam[i][1].push_back(c);
            sam.insert(c,i);
        }
    }
    sam.build();
    for(int c,x,i=1;i<=m;i++)
    {
        scanf("%d",&x);
        int u=1;
        while(x--)
        {
            scanf("%d",&c);
            if(p[u].trs.find(c)==p[u].trs.end()){u=0;}
            u=p[u].trs[c];
        }
        if(u)
        {
            ct++;
            que[ct].l = tin[u];
            que[ct].r = tout[u];
            que[ct].id = i;
            v[u]++;
        }
    }
    sort(que+1,que+1+ct,cmp);
    for(int k=1,i=1;i<=tim;i++)
    {
        for(int j=0;j<p[pla[i]].ve.size();j++)
        {
            up(i,1);
            up(las[p[pla[i]].ve[j]],-1);
            las[p[pla[i]].ve[j]]=i;
        }
        int tmp = down(i);
        for(;que[k].r==i;k++)
            ans[que[k].id] = tmp - down(que[k].l-1);
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    for(int i=1;i<=n;i++)
    {
        int c = 0,u=1;
        for(int j=0;j<nam[i][0].size();j++)
        {
            u = p[u].trs[nam[i][0][j]];
            int tmp = u;
            while(tmp!=1&&vis[tmp]<i)
            {
                vis[tmp]=i;
                c+=v[tmp];
                tmp=p[tmp].pre;
            }
        }
        u=1;
        for(int j=0;j<nam[i][1].size();j++)
        {
            u = p[u].trs[nam[i][1][j]];
            int tmp = u;
            while(tmp!=1&&vis[tmp]<i)
            {
                vis[tmp]=i;
                c+=v[tmp];
                tmp=p[tmp].pre;
            }
        }
        printf("%d ",c);
    }
    printf("\n");
    return 0;
}

 

posted @ 2018-09-22 11:20  LiGuanlin  阅读(219)  评论(2编辑  收藏  举报