【CF778C】Peterson Polyglot(Trie树,启发式合并)

题意:有一棵n个结点的只由小写字母组成的Trie树,给定它的具体形态,问删除哪一层后剩下Trie树的结点数最少

n<=3e5

思路:先建出原Trie树,对于每一层的每一个结点计算删除后对答案的贡献,这一部分使用启发式合并

官方题解证明了时间复杂度是一个log的

http://codeforces.com/blog/entry/50724

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 typedef long long ll;
 6 using namespace std;
 7 #define N 310000
 8
 9 int map[N][27],head[N],vet[N<<1],nxt[N<<1],size[N],dep[N],ans[N],flag[N],
10     n,cnt,tot;
11
12 char len[N<<1],ch[10];
13
14 void add(int a,int b,char c)
15 {
16     nxt[++tot]=head[a];
17     vet[tot]=b;
18     len[tot]=c;
19     head[a]=tot;
20 }
21
22 int merge(int x,int y)
23 {
24     if(!x||!y) return x+y;
25     int t=++cnt;
26     size[t]=1;
27     for(int i=1;i<=26;i++)
28     {
29         map[t][i]=merge(map[x][i],map[y][i]);
30         size[t]+=size[map[t][i]];
31     }
32     return t;
33 }
34
35 void dfs(int u)
36 {
37     size[u]=flag[u]=1;
38     int e=head[u];
39     while(e)
40     {
41         int v=vet[e];
42         if(!flag[v])
43         {
44             dep[v]=dep[u]+1;
45             map[u][len[e]-‘a‘+1]=v;
46             dfs(v);
47             size[u]+=size[v];
48         }
49         e=nxt[e];
50     }
51     ans[dep[u]]+=size[u];
52     cnt=n;
53     int t=0;
54     for(int i=1;i<=26;i++) t=merge(t,map[u][i]);
55     ans[dep[u]]-=max(size[t],1);
56 }
57
58 int main()
59 {
60     scanf("%d",&n);
61     for(int i=1;i<=n-1;i++)
62     {
63         int x,y;
64         scanf("%d%d%s",&x,&y,ch);
65         add(x,y,ch[0]);
66         add(y,x,ch[0]);
67     }
68     memset(flag,0,sizeof(flag));
69     dfs(1);
70     int k=0;
71     for(int i=0;i<=n-1;i++)
72      if(ans[k]<ans[i]) k=i;
73     printf("%d\n",n-ans[k]);
74     printf("%d\n",k+1);
75     return 0;
76 }

原文地址:https://www.cnblogs.com/myx12345/p/9917049.html

时间: 2024-10-08 23:43:46

【CF778C】Peterson Polyglot(Trie树,启发式合并)的相关文章

[bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值. 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为"Q x y k"或者"L x y ",其含义见题目描述部分. Output 对于每一个第一类

【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值.  接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分. Output 对于每

HDU6191 Query on A Tree (01字典树+启发式合并)

题意: 给你一棵1e5的有根树,每个节点有点权,1e5个询问(u,x),问你子树u中与x异或最大的值是多少 思路: 自下而上启发式合并01字典树,注意合并时清空trie 线段树.字典树这种结构确定的数据结构,启发式合并的时候不需要考虑次序,复杂度都是nlogn 代码: 2200 / 10000ms , 60 / 128 M #include<iostream> #include<cstdio> #include<algorithm> #include<cmath&

HDU6191(01字典树启发式合并)

Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 801    Accepted Submission(s): 302 Problem Description Monkey A lives on a tree, he always plays on this tree. One day, monkey

【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时间复杂度为O(n*log),因为每个的节点只会作为小的部分合并,因此他所在的一小部分至少变大2倍,对于每一个节点他作为被合并方最多log次,因此其复杂度为O(n*log),而这个是绝对跑不满还差很多的,我们视他为无常数就好了,当然这一切都是建立在无拆分操作的基础之上,只要有了拆分启发式合并的复杂度就

BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并

题目大意:给出一个森林,每个节点都有一个权值.有若干加边操作,问两点之间路径上的第k小权值是多少. 思路:这题和COT1比较像,但是多了连接操作.这样就只能暴力合并连个树.启发式合并会保证时间复杂度不至于太大.然后就是用可持久化线段树维护一个树的信息,按照dfs序来建树,每个节点的可持久化链的参考版本就是它父亲的版本.之后利用权值线段树可区间加减的特性,用f[x] + f[y] - f[lca] - f[father[lca]]来计算权值. CODE: #include <cstdio> #i

数据结构(trie,启发式合并):HDU 5841 Alice and Bob

这个对数字建二进制trie,然后启发式合并,最重要的是Push_up()操作和Merge()操作,解决了复杂度的问题. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N=100010,M=2000010; 6 int cnt,fir[N],to[N*2],nxt[N*2]; 7 void addedge(int a,int

【主席树启发式合并】【P3302】[SDOI2013]森林

Description 给定一个 \(n\) 个节点的森林,有 \(Q\) 次操作,每次要么将森林中某两点联通,保证操作后还是个森林,要么查询两点间权值第 \(k\) 小,保证两点联通.强制在线. Limitation \(1~\leq~n,~Q~\leq~80000\) Solution 考虑有连边还有查询链上第 \(k\) 大,于是要么用 LCT,要么用主席树. 考虑如果用 LCT 的话,并不能快速的维护两点间链的信息(其实感觉在access的时候乱搞一下有希望在多一个 \(\log\) 的

主席树+启发式合并(LT) BZOJ3123

好久没做题了,写道SBT又RE又T 查询:主席树裸题. 修改:对于两个树合并重建小的树. 注意fa[x][i]重新计算时要清空 #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; inline int read() { char c=getchar();int x=0,sig=1; for(;!isdigit(c);c=get

p3302 [SDOI2013]森林(树上主席树+启发式合并)

对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和维护主席树即可 然后树上主席树就是维护节点到根节点的信息即可, 询问链上的第k大时,画图后可以发现维护一个rootx,rooty,rootlca和rootfalca即可解决问题 注意空间要开够 然后注意细节,没了 #include <cstdio> #include <cstring>