BZOJ 3012 [Usaco2012 Dec]First! wzq脑洞hash树(正解trie树)

博客风格转化计划实验篇2



题意:

给n(n<=30000)个字符串,所有字符串长度加起来不超过300000(字符串只含有小写字母)。

求解哪些字符串可以称作第一字符串。

一个字符串能被称为第一字符串的条件为存在一种字典序使它排在第一名。

方法:

wzq脑洞hash树…..

然而并没有trie树优越。

并且我脑洞的这个树好处有啥:暂且不知道。

坏处有啥:很容易被卡,自带常数。

所以为什么要这么写?只是因为我跟wjc说这题我特么一定要用带hash的东西搞过去!

*目标达成√

解析:

对于以下内容,请不要吐槽这个树如何辣鸡,因为我也知道这个树很辣鸡,不过自己立下的flag流着泪也要写完。



先讲正常做法。

我们首先搞出来trie树,然后对于每一个串,我们遍历它,如果路径上有其他串的结尾点,则该串一定不会被选,否则对于每一层该串的点,我们向该层其他存在的点连一条有向边。

所有边连完之后,我们对于之前连的那些边跑一个拓扑,找是否有环出现,如果有环出现则一定不会选取该串,否则该串可选。

然后我们来讲wzq脑洞树



这个树是怎么实现的呢…假如说有一个字符串abbc。

这个串的插入过程是什么呢?

初始hash值是0,第一个字母是a

所以插入(0,a),hash值变为x

接着插入(x,b),hash值变为y

然后插入(y,b),hash值变为z

继续插入(z,c),hash值变为整个串的hash值

最后插入(hash,-1)。

然后做法跟trie树类似辣.



立个flag

也许有一天我就发现这个东西的优点了呢?

代码:

没写trie树,看脑洞树吧

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 300100
#define M 3000100
#define base 131
#define mod 10000007
using namespace std;
typedef unsigned long long ull;
int n;
char all[M+N];
int fir[N];
int hash[27];
int head[mod];
int head2[50];
int cnt2;
int boom[N];
int map[30][30];
struct node
{
    int from,to,next,lenth;
    ull val;
}edge[N],edge2[N];
int cnt;
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void init2()
{
    memset(map,0,sizeof(map));
    memset(head2,-1,sizeof(head2));
    cnt2=1;
}
void edgeadd2(int from,int to)
{
    edge2[cnt2].from=from,edge2[cnt2].to=to;
    edge2[cnt2].next=head2[from];
    head2[from]=cnt2++;
}
void edgeadd(int from,int to,ull val)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int find(ull x,int no)
{
    int szy=x%mod;
    for(int i=head[szy];i!=-1;i=edge[i].next)
    {
        if(edge[i].val==x&&edge[i].to==no){return 1;}
    }
    return 0;
}
void ins(ull x,int no)
{
    int szy=x%mod;
    edgeadd(szy,no,x);
}
int in[30];
int find2(int alpha1,int alpha2)
{
    for(int i=head2[alpha1];i!=-1;i=edge2[i].next)
    {
        if(edge2[i].to==alpha2)return 1;
    }
    return 0;
}
void work(ull x,int alpha)
{
    int szy=x%mod;
    for(int i=head[szy];i!=-1;i=edge[i].next)
    {
        if(edge[i].val==x&&edge[i].to!=alpha&&!map[alpha][edge[i].to])
            edgeadd2(alpha,edge[i].to),in[edge[i].to]++;
    }
}
int vvvv[30];
int sta[100];
bool check()
{
    int l=1,r=0;
    for(int i=1;i<=26;i++)
        if(!in[i])sta[++r]=i;
    while(l<=r)
    {
        int u=sta[l++];
        for(int i=head2[u];i!=-1;i=edge2[i].next)
        {
            int to=edge2[i].to;
            in[to]--;
            if(!in[to])sta[++r]=to;
        }
    }
    return r==26;
}
int tail[N];
int main()
{
    init();
    scanf("%d",&n);
    int la=0;
    for(int i=1;i<=n;i++)
    {
        fir[i]=la+1;
        scanf("%s",all+la+1);
        int len=strlen(all+la+1);
        tail[i]=la+len;
        la+=len+1;
    }
    for(int i=1;i<=n;i++)
    {
        ull hasha=0;
        for(int j=fir[i];j<=tail[i];j++)
        {
            if(!find(hasha,all[j]-‘a‘+1))
                ins(hasha,all[j]-‘a‘+1);
            hasha=hasha*base+all[j];
        }
        ins(hasha,-1);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        init2();
        memset(in,0,sizeof(in));
        ull hasha=0;
        for(int j=fir[i];j<=tail[i];j++)
        {
            if(find(hasha,-1)){boom[i]=1;break;}
            work(hasha,all[j]-‘a‘+1);
            hasha=hasha*base+all[j];
        }
        if(!check())boom[i]=1;
        if(!boom[i])ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)
    {
        if(!boom[i])printf("%s\n",all+fir[i]);
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-15 21:18:43

BZOJ 3012 [Usaco2012 Dec]First! wzq脑洞hash树(正解trie树)的相关文章

BZOJ 3011: [Usaco2012 Dec]Running Away From the Barn( dfs序 + 主席树 )

子树操作, dfs序即可.然后计算<=L就直接在可持久化线段树上查询 ------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; #define M(l, r) (((l) + (r)) >> 1) const int maxn = 200009; typedef long long ll; inline ll

BZOJ 3011 Usaco2012 Dec Running Away From the Barn 可并堆

题目大意:给定一棵有根树,求以每个点为根的子树中有多少点到它的距离不超过l 第一眼是可并堆- - 于是怒写- - 管它正解是啥- - 从下到上维护可并大根堆 键值是该点到当前根节点的距离 一旦堆顶剪枝大于l就弹顶 时间复杂度O(nlogn) 什么?你说将整个堆都加上一个值? 打标记不就好了- - 毫无疑问可并堆是可以打标记的- - 此外我的随机堆写if(flag^=1)就T写if(rand()&1)就秒过是什么鬼- - #include <cstdio> #include <cs

BZOJ 2741【FOTILE模拟赛】L 分块+可持久化Trie树

题目大意 给出一个序列,求[l, r]中的最大连续xor 和. 强制在线 思路 先把整个序列分成n  √  块,预处理每一块的开头到每个数字的最大连续xor 和.这个我们只需处理出前缀xor 和,之后用可持久化Trie树就可以搞定.这样询问的右边就是整块的了.剩下左边的随便暴力一下就能过了.. CODE #define _CRT_SECURE_NO_WARNINGS #include <cmath> #include <cstdio> #include <cstring>

Bzoj 1537: [POI2005]Aut- The Bus 题解 [由暴力到正解]

1537: [POI2005]Aut- The Bus Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 387  Solved: 264[Submit][Status][Discuss] Description Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 到 m编号. 每个路口用两个数(i, j) 表示(1 <= i <= n, 1 <= j

BZOJ 2741 【FOTILE模拟赛】L 分块+可持久化Trie树

题目大意:给定一个序列,多次询问[l,r]中最大子序异或和 强制在线 一直RE的同学注意,本题的强制在线如果直接加会爆int导致调用数组下标为负 首先我们有一个转化 维护前缀异或和数组a[] 那么[l,r]中最大子序异或和就是a数组中[l-1,r]中任取两个数的最大异或值 然后分块处理 对于每块的第一个数a[i] 我们依次处理出对于所有的j>=i的[i,j]中的最大异或值 即s[i][j]=max(a[j]与i~j所有数中的最大异或值,s[i][j-1]) 然后对于每个询问[l,r],设l后面第

海量路由表可以使用HASH表存储吗-HASH查找和TRIE树查找

千万别!很多人这样说,也包括我.Linux内核早就把HASH路由表去掉了,现在就只剩下TRIE了,不过我还是希望就这两种数据结构展开一些形而上的讨论. 1.hash和trie/radix hash 和tire其实是可以统一在一起的.具有相同hash值的多个项具有一个共同的特征,这个特征怎么提取呢?无疑这就是hash函数的工作.而trie树 (或者radix树,管它呢)的一棵子树也有共同的特征,这个特征怎么提取呢?无疑这就是该子树根节点的父节点指示的某些bits在这棵子树的每一个节点 都具有相同的

【BZOJ3011】[Usaco2012 Dec]Running Away From the Barn 可并堆

[BZOJ3011][Usaco2012 Dec]Running Away From the Barn Description It's milking time at Farmer John's farm, but the cows have all run away! Farmer John needs to round them all up, and needs your help in the search. FJ's farm is a series of N (1 <= N <=

bzoj3011[Usaco2012 Dec]Running Away From the Barn*

bzoj3011[Usaco2012 Dec]Running Away From the Barn 题意: 给出以1号点为根的一棵有边权的树,问每个点的子树中与它距离小于l的点有多少个.树的大小≤200000. 题解: 每个节点维护一个带标记可并堆,dfs时对子节点的堆加上当前节点到该子节点的边权,之后令其与当前节点的堆合并. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #i

bzoj 1590: [Usaco2008 Dec]Secret Message 秘密信息

1590: [Usaco2008 Dec]Secret Message 秘密信息 Description 贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息. 信息是二进制的,共有M(1≤M≤50000)条.反间谍能力很强的约翰已经部分拦截了这些信息,知道了第i条二进制信息的前bi(l<bi≤10000)位.他同时知道,奶牛使用N(1≤N≤50000)条密码.但是,他仅仅了解第J条密码的前cj(1≤cj≤10000)位. 对于每条密码J,他想知道有多少截得的信息能够和它匹配.也就是说,有多