bzoj 2434 阿狸的打字机 fail树的性质

如果a串是另b串的后缀,那么在trie图上沿着b的fail指针走一定可以走到a串。

而a串在b串里出现多少次就是它是多少个前缀的后缀。

所以把fail边反向建树维护个dfs序就行了。

并不是很难。。。但没想出来TAT

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #define N 200005
  6 #define inf 0x3f3f3f3f
  7 using namespace std;
  8 int n;
  9 int ch[N][2], fa[N];
 10 int a[N];
 11 int mn[N],zhi[N],size[N];
 12 void push_up(int x)
 13 {
 14     size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
 15     mn[x] = min(zhi[x], min(mn[ch[x][0]], mn[ch[x][1]])); return;
 16 }
 17 int rev[N];
 18 void push_down(int x)
 19 {
 20     if (rev[x])
 21     {
 22         rev[x] ^= 1; rev[ch[x][1]] ^= 1; rev[ch[x][0]] ^= 1;
 23         swap(ch[x][0], ch[x][1]);
 24     }
 25     return;
 26 }
 27 void rotate(int p)
 28 {
 29     int q = fa[p], y = fa[q], x = (ch[q][1] == p);
 30     ch[q][x] = ch[p][x ^ 1]; fa[ch[q][x]] = q;
 31     ch[p][x ^ 1] = q; fa[q] = p;
 32     fa[p] = y;
 33     if (y)
 34     {
 35         if (ch[y][1] == q)ch[y][1] = p;
 36         else ch[y][0] = p;
 37     }
 38     push_up(q); return;
 39 }
 40 int root;
 41 void splay(int x,int yy)
 42 {
 43     for (int y; y = fa[x]; rotate(x))
 44     {
 45         if (y == yy)break;
 46         if (fa[y] != yy)
 47         {
 48             if ((ch[fa[y]][0] == y) ^ (ch[y][0] == x))rotate(x);
 49             else rotate(y);
 50         }
 51     }
 52     push_up(x);
 53     if (!yy)root = x;
 54 }
 55 int cnt;
 56 int find(int k, int x)
 57 {
 58     push_down(k);
 59     if (mn[ch[k][0]] == x)
 60     {
 61         return find(ch[k][0], x);
 62     }
 63     if (zhi[k] == x)return size[ch[k][0]] + 1;
 64     return find(ch[k][1], x) + size[ch[k][0]] + 1;
 65 }
 66 int fd(int k, int x)
 67 {
 68     push_down(k);
 69     if (size[ch[k][0]] + 1 == x)return k;
 70     if (size[ch[k][0]] + 1 >= x)return fd(ch[k][0],x);
 71     return fd(ch[k][1], x - size[ch[k][0]] - 1);
 72 }
 73 struct node
 74 {
 75     int yuan,z;
 76     friend bool operator < (node aa,node bb)
 77     {
 78         if(aa.z!=bb.z)return aa.z<bb.z;
 79         return aa.yuan<bb.yuan;
 80     }
 81 }s[N];
 82 int yin[N];
 83 int main()
 84 {
 85     scanf("%d", &n); mn[0] = inf;
 86     for (int i = 1; i <= n; i++)
 87     {
 88         s[i].yuan=i;scanf("%d",&s[i].z);
 89     }
 90     sort(s+1,s+n+1);
 91     for(int i=1;i<=n;i++)
 92     {
 93         a[s[i].yuan]=i;
 94     }
 95     root = 1; a[n + 1] = inf;
 96     ch[1][1] = 2; zhi[1] = a[1]; size[1] = n + 1;
 97     for (int i = 2; i <= n + 1; i++)
 98     {
 99         size[i] = n - i + 2;
100         zhi[i] = a[i]; fa[i] = i - 1; ch[i - 1][1] = i;
101     }
102     for (int i = n + 1; i >= 1; i--)push_up(i);
103     splay(n, 0);
104     for (int i = 1; i <= n; i++)
105     {
106         int y = find(root, mn[root]);
107         if(i!=n)printf("%d ",y+(i-1));
108         else printf("%d",y+(i-1));
109         int t = fd(root, y+1);
110         splay(t, 0);
111         rev[ch[t][0]] ^= 1;
112         t = fd(root, 2);
113         splay(t, 0);
114         ch[t][0] = 0;
115         push_up(t);
116     }
117     puts("");
118     return 0;
119 }
时间: 2024-10-09 12:55:20

bzoj 2434 阿狸的打字机 fail树的性质的相关文章

BZOJ 2434 NOI2011 阿狸的打字机 fail树+树状数组

题目大意:初始字串为空,首先给定一系列操作序列,有三种操作: 1.在结尾加一个字符 2.在结尾删除一个字符 3.打印当前字串 然后多次询问第x个打印的字串在第y个打印的字串中出现了几次 卡了很久--到底还是对AC自动机了解不是很深啊QAQ fail树不是很难想 至少在用AC自动机切掉3172之后不是很难想-- 首先构建AC自动机 注意由于这个字串的特殊构造 我们不必每打印一个字符串再插入Trie树 令now为当前指针 初始为root 考虑三种操作 在结尾添加一个字符->新建一个子节点(若存在在不

AC自动机:BZOJ 2434 阿狸的打字机

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1834  Solved: 1053[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽

BZOJ 2434 阿狸的打字机

题解网上都有...要注意下细节... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxn 100500 #define maxv 100500 #define maxe 200500 using namespace std; int ls,m,x,y,son[maxn][27],root,tot

BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status][Discuss] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6

BZOJ 3881 Coci2015 Divljak fail树+树链的并

题目大意:给定两个字符串集合S和T,初始给定S集合中的所有字符串,不断向T集合中添加字符串,以及询问S集合中的某个字符串在T集合中的多少个字符串中出现过 神题- - 首先对S集合的所有字符串构建fail树 T集合中每加入一个字符串,我们就将这个字符串在AC自动机上跑一遍,并记录经过的所有节点 根据fail树的性质,这些节点到根路径上的所有节点的并集的出现次数都加了1 因此我们要求的就是树链的并 求法如下: 将所有节点按照DFS序排序 每个点到根的路径上的所有节点权值+1 相邻两个点的LCA到根的

Trie图和Fail树

Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造next[cur][i]的fail指针,cur为父节点,next[cur][i]为cur的儿子结点,如果是AC自动机,如果父亲结点tmp(tmp是cur的一份拷贝)的next[fail[tmp]][i]不存在时,需要让tmp不断回溯(即tmp = fail[tmp]),直到next[fail[tmp]]

BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

bzoj 2434 [Noi2011]阿狸的打字机(fail树+离线处理+BIT)

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1861  Solved: 1070[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ) ------------------------------------------------------------------------------------------- #include<cstdio>