【Trie+vector】BZOJ4896(Thu Summer Camp2016)[补退选]题解

题目概述

有n个事件,分为三类:
1.插入字符串S。
2.删除字符串S。
3.求最小的pos,使第pos个事件结束后,以S为前缀的字符串个数>v。

解题报告

这种插入和删除字符串还要问前缀(而且还有强制在线)的题目我们不难想到用Trie解决。只是这个询问比较奇怪,但用STL乱搞就很容易了(滑稽:P)。

为每一个Trie节点记录一个vector,表示w分别为1,2,3,…时的最早事件编号,那么每次询问v时输出w为v+1时的最早事件编号就行了(因为每次w只会+1,所以v+1的最早事件编号绝对比>v+1的最早事件编号小),非常暴力……

但是这样vector会不会炸空间呢?不会。思考一下我们会发现只有插入的时候vector才会进入新的元素,删除是不会的(因为删除让w减小了)。而每次插入最多遍历到len(len是字符串长度)个节点,所以所有节点vector元素的总数为n*len,是可以承受的。

ps:这道题题目描述自相矛盾,没有说清楚到底是26个小写字母还是10个小写字母,不过好像用指针动态处理就没什么事了(但我还是开了10=_=)

示例程序

#include<cstdio>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=100000,maxl=60,maxt=maxn*maxl,maxi=10;

int te,ans;
char s[maxl+5];
struct Trie
{
    struct node
    {
        int w,id;node* son[maxi];vector<int> que;
        node(int a,int b) {w=a;id=b;for (int i=0;i<maxi;i++) son[i]=0;}
    };
    typedef node* P_node;
    int si;P_node ro;
    Trie() {si=0;ro=new node(0,0);}
    int ID(char ch) {return ch-'a';}
    void Insert(char *s,int te)
    {
        P_node pos=ro;
        for (int i=1;s[i];i++)
        {
            if (!pos->son[ID(s[i])]) pos->son[ID(s[i])]=new node(0,++si);
            pos=pos->son[ID(s[i])];pos->w++;
            int num=pos->que.size();
            if (!num||pos->w>num) pos->que.push_back(te);
            //新的w出现了,把事件编号加入vector
        }
    }
    void Delete(char *s)
    {
        P_node pos=ro;
        for (int i=1;s[i];i++) pos=pos->son[ID(s[i])],pos->w--;
    }
    int Find(char *s,int v)
    {
        P_node pos=ro;
        for (int i=1;s[i];i++)
            if (!pos->son[ID(s[i])]) return -1; else
            pos=pos->son[ID(s[i])];
        int num=pos->que.size();
        if (num<=v) return -1;
        return pos->que[v]; //输出v+1的最小事件编号
    }
};
Trie tr;

char readc()
{
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; else return *l++;
}
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int reads(char *s)
{
    int len=0;char ch=readc();if (ch==EOF) return EOF;
    s[++len]=ch;while ('a'<=s[len]&&s[len]<='z') s[++len]=readc();
    s[len--]=0;return len;
}
int readi(int &x)
{
    int tot=0,f=1;char ch=readc(),lst=ch;
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=readc();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=readc();
    x=tot*f;
    return Eoln(ch);
}
int absi(int x) {if (x<0) return -x; else return x;}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(te);
    for (int i=1;i<=te;i++)
    {
        int td;readi(td);reads(s);
        if (td==1) tr.Insert(s,i); else
        if (td==2) tr.Delete(s); else
        {
            int a,b,c;readi(a);readi(b);readi(c);
            printf("%d\n",ans=tr.Find(s,((LL)a*absi(ans)+b)%c));
        }
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值