BZOJ 3926 诸神眷顾的幻想乡

开始看错题看成了每个点度数不超过20 后来翻了翻题解原来看错题的不止我一个

既然叶子数量不超过20,考虑树上的任何一条路径,以任何点为根时,如果它不是一条从上到下的路径,那么以它的任意一端的子树内的某一个叶子为根必然可以变成从上到下的。否则,以它处于下端的点的子树内的叶子为根也可以做到。

所以如果以每一个叶子为根的串建成个 Trie 并且把所有单词丢进广义SAM就做完了。

说一下广义SAM的构建:

  • 最简单轻松的,如果只是插入一些字符串,那么插入一个后把 last 设置为根就好了。

    据说这种构造在某些时候是会死掉的但是好写啊,如果单词重复据说会加多点。。但是平时写由于很难出错一直写的这个。

  • 标准的,对于所有串构造一棵 Trie 然后 BFS 这个 Trie 来构造。加入一个点的时候就把 last 设置为它父亲加入完时的 last。

    注意,写 DFS 是会被卡的!可以看这篇博客

空间开小WA了发海星

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include "queue"
using namespace std;
typedef long long ll;
#define MAXN 2000006 int n , lst , m;
char ch[MAXN]; struct SAM{
int son[MAXN][11]; // sons
int par[MAXN] , len[MAXN]; // node
int cnt , ecnt;
void init( ) {
memset( son , 0 , sizeof son );
cnt = lst = 1; ecnt = 0;
}
void ins( int x ) {
int cur = ++ cnt;
len[cur] = len[lst] + 1;
int p = lst;
while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
if( !p ) par[cur] = 1;
else {
int q = son[p][x];
if( len[q] == len[p] + 1 ) par[cur] = q;
else {
int cl = ++ cnt;
memcpy( son[cl] , son[q] , sizeof son[q] );
par[cl] = par[q]; // copy
len[cl] = len[p] + 1 , par[q] = par[cur] = cl;
for( ; son[p][x] == q ; p = par[p] ) son[p][x] = cl;
}
}
lst = cur;
}
int ch[MAXN][10] , pos[MAXN];
void build( ) {
init( );
queue<int> Q;
Q.push( 0 ); pos[0] = 1;
while( !Q.empty() ) {
int u = Q.front(); Q.pop();
for( int i = 0 ; i < 10 ; ++ i ) if( ch[u][i] ) {
Q.push( ch[u][i] );
lst = pos[u]; ins( i ); pos[ch[u][i]] = lst;
}
}
}
long long work( ) {
long long res = 0;
for( int i = 2 ; i <= cnt ; ++ i ) {
res += len[i] - len[par[i]];
}
return res;
}
} S ;
namespace wtf { int n , m; int head[MAXN] , to[MAXN << 1] , nex[MAXN << 1] , ecn;
void ade( int u , int v ) {
to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn;
} int w[MAXN] , cn; void build( int u , int ps , int fa ) {
for( int i = head[u] ; i ; i = nex[i] ) {
int v = to[i];
if( v == fa ) continue;
int& x = S.ch[ps][w[v]];
if( !x ) x = ++ cn;
build( v , x , u );
}
}
int c;
void main() {
cin >> n >> c;
for( int i = 1 ; i <= n ; ++ i )
scanf("%d",&w[i]);
for( int i = 1 , u , v ; i < n ; ++ i )
scanf("%d%d",&u,&v) , ade( u , v ) , ade( v , u );
S.init();
for( int i = 1 ; i <= n ; ++ i )
if( !nex[head[i]] ) build( i , S.ch[0][w[i]] ? S.ch[0][w[i]] : ( S.ch[0][w[i]] = ++ cn ) , i );
S.build();
cout << S.work( ) << endl;
}
}
int main() {
wtf::main();
}

最新文章

  1. 扩展BindingList,防止增加、删除项时自动更新界面而不出现“跨线程操作界面控件 corss thread operation”异常
  2. Android5.1.1 - APK签名校验分析和修改源码绕过签名校验
  3. VideoToolbox硬件编解码H.264视频流错误码
  4. 理解Java接口
  5. hashMap的输出是和加入元素的顺序一样的吗?
  6. 2016-2017 ACM-ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred) 几道简单题的题解
  7. VS2012 中使用Emacs布局
  8. DB2 相关操作
  9. CXF 开发 WebService
  10. [概念] js的函数节流和throttle和debounce详解
  11. 使用Visual Studio 2008创建你的第一个Windows Mobile程序介绍
  12. PL/SQL 编程(二)游标、存储过程、函数
  13. 凸包GiftWrapping GrahamScan 算法实现
  14. underscore.js 源码阅读 准备
  15. mysql与emoji和特殊字符
  16. MATLAB 程序处理结果出现 NAN 问题
  17. C#析构函数与Dispose
  18. 最佳实战Docker持续集成图文详解
  19. swift闭包 notes http://www.gittielabs.com
  20. 事件处理程序DOM0,DOM2,IE的区别总结

热门文章

  1. 脚本注入2(post)
  2. 《Spring源码深度解析》学习笔记——Spring的整体架构与容器的基本实现
  3. 热身训练1 Problem B. Harvest of Apples
  4. Python课程笔记 (五)
  5. Celery Task(定时任务)及参数
  6. Luogu P3758 [TJOI2017]可乐 | 矩阵乘法
  7. JAVA笔记13__创建线程/线程休眠/等待线程终止/线程中断/守护线程
  8. html5的Message信息提示框
  9. uni-app(Vue)中(picker)用联动(关联)选择以至于完成某些功能
  10. .net core 和 WPF 开发升讯威在线客服系统:把 .Net Framework 打包进安装程序