题解

我们发现第一种操作肯定不可取,每个节点里它最近的点是它最长出现过的后缀,发现这就是AC自动机的fail节点,根据fail的关系这会是一棵树,而一个单词的前一个序号最大的后缀必定是它的父亲

然后我们考虑怎么获得最小值,x是肯定要加上的,我们让每次减掉的y最小

一个错误的想法:按照儿子个数分类,建一个set,每次拿儿子最小的= =

emmm这个,如果你有一条10^3的链和1个点5个儿子比较一下,会发现这是错的

我们不拿这个点会造成多少贡献来看,我们初始设置每个点要减去的值都是0,我们挑选了一个点,这个点所在子树除外的其他子树,要减去的值都会+1

这样的话,我们只要每次找出所有能选的点,用set维护一下,然后每次拿一个子树大小最小的点作为这个位置上填的单词就可以了

【AC自动机写挂了一次,以前都是26个指针填满,但是这回是链前,必须遍历过每个fail直到找到对应的字符边】

代码

#include <bits/stdc++.h>
//#define ivorysi
#define MAXN 510005
typedef long long int64;
typedef unsigned int u32;
using namespace std;
struct node{
int to,next,val;
}edge[MAXN * 2];
int fail[MAXN],rt = 1,head[MAXN],sumE,nodecnt = 1,ed[MAXN],siz[MAXN];
int n,len,id[MAXN],fa[MAXN];
char s[MAXN];
bool vis[MAXN];
vector<int> g[MAXN],son[MAXN];
int que[MAXN],tot;
struct data {
int id;
friend bool operator < (const data &a,const data &b) {
return siz[a.id] < siz[b.id] || (siz[a.id] == siz[b.id] && a.id < b.id);
}
friend bool operator == (const data &a,const data &b) {
return a.id == b.id;
}
};
set<data> S;
void add(int u,int v,int c) {
edge[++sumE].to = v;
edge[sumE].next = head[u];
edge[sumE].val = c;
head[u] = sumE;
}
int find_edge(int u,int c) {
for(int i = head[u] ; i ; i = edge[i].next) {
if(edge[i].val == c) return edge[i].to;
}
return 0;
}
queue<int> Q;
void build_ACAM() {
Q.push(1);
fail[1] = 1;
while(!Q.empty()) {
int u = Q.front();Q.pop();
for(int i = head[u] ; i ; i = edge[i].next) {
int v = edge[i].to;
if(u == 1) fail[v] = u;
else {
int t = fail[u];
int x = 0;
while(1) {
x = find_edge(t,edge[i].val);
if(x > 0) break;
if(t == 1) break;
t = fail[t];
}
fail[v] = x > 0 ? x : 1;
}
Q.push(v);
g[fail[v]].push_back(v);
}
}
}
void ins(int id) {
int p = rt;
for(int i = 1 ; i <= len ; ++i) {
int v = find_edge(p,s[i] - 'a');
if(v == 0) {
v = ++nodecnt;
add(p,v,s[i] - 'a');
}
p = v;
}
ed[p] = id;
}
void Init() {
scanf("%d",&n);
for(int i = 1 ; i <= n ; ++i) {
scanf("%s",s + 1);
len = strlen(s + 1);
ins(i);
}
build_ACAM();
}
void dfs(int u,int last_f) {
if(ed[u]) {que[++tot] = ed[u];siz[ed[u]] = 1;}
if(last_f && ed[u]) {
son[last_f].push_back(ed[u]);
fa[ed[u]] = last_f;
}
for(auto k : g[u]) {
if(ed[u]) dfs(k,ed[u]);
else dfs(k,last_f);
}
}
void Solve() {
ed[1] = n + 1;
dfs(1,0);
int64 ans = 1LL * n * (n + 1) / 2;
for(int i = tot ; i >= 1 ; --i) {
siz[fa[que[i]]] += siz[que[i]];
}
S.insert((data){ed[1]});
for(int i = 0 ; i <= n ; ++i) {
auto it = S.begin();
int x = (*it).id;
S.erase(it);
id[x] = i;
ans -= id[fa[x]];
for(auto k : son[x]) {
S.insert((data){k});
}
}
printf("%lld\n",ans);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
}

【LOJ】#2012. 「SCOI2016」背单词的更多相关文章

  1. loj#2012. 「SCOI2016」背单词

    题目链接 loj#2012. 「SCOI2016」背单词 题解 题面描述有点不清楚. 考虑贪心 type1的花费一定不会是优的,不考虑, 所以先把后缀填进去,对于反串建trie树, 先填父亲再填儿子, ...

  2. AC日记——「SCOI2016」背单词 LiBreOJ 2012

    #2012. 「SCOI2016」背单词 思路: Orz: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1 ...

  3. 「SCOI2016」背单词 解题报告

    「SCOI2016」背单词 出题人sb 题意有毒 大概是告诉你,你给一堆n个单词安排顺序 如果当前位置为x 当前单词的后缀没在这堆单词出现过,代价x 这里的后缀是原意,但不算自己,举个例子比如abc的 ...

  4. 「SCOI2016」背单词

    「SCOI2016」背单词 Lweb 面对如山的英语单词,陷入了深深的沉思,「我怎么样才能快点学完,然后去玩三国杀呢?」.这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,然后 ...

  5. loj2012 「SCOI2016」背单词

    -- #include <algorithm> #include <iostream> #include <cstring> #include <cstdio ...

  6. loj#2013. 「SCOI2016」幸运数字 点分治/线性基

    题目链接 loj#2013. 「SCOI2016」幸运数字 题解 和树上路径有管...点分治吧 把询问挂到点上 求出重心后,求出重心到每个点路径上的数的线性基 对于重心为lca的合并寻味,否则标记下传 ...

  7. loj#2015. 「SCOI2016」妖怪 凸函数/三分

    题目链接 loj#2015. 「SCOI2016」妖怪 题解 对于每一项展开 的到\(atk+\frac{dnf}{b}a + dnf + \frac{atk}{a} b\) 令$T = \frac{ ...

  8. loj#2016. 「SCOI2016」美味

    题目链接 loj#2016. 「SCOI2016」美味 题解 对于不带x的怎么做....可持久化trie树 对于带x,和trie树一样贪心 对于答案的二进制位,从高往低位贪心, 二进制可以表示所有的数 ...

  9. loj #2013. 「SCOI2016」幸运数字

    #2013. 「SCOI2016」幸运数字 题目描述 A 国共有 n nn 座城市,这些城市由 n−1 n - 1n−1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以 ...

随机推荐

  1. 手脱nSPack 2.1 - 2.5

    1.载入PEID 使用核心扫描出的结果 nSPack 2.1 - 2.5 -> North Star/Liu Xing Ping 2.载入OD,一进来就是一个大跳转,F8跟着走 >- E9 ...

  2. 手脱Upack 2.x - 3.x

    1.PEID查壳 Upack 2.x - 3.x Heuristic Mode -> Dwing 2.载入OD,一上来就是一个大跳转,先F8跟一会 >- E9 56D40300 jmp 跑 ...

  3. 「Django」rest_framework学习系列-渲染器

    渲染器:作用于页面,JSONRenderer只是JSON格式,BrowsableAPIRenderer有页面,.AdminRenderer页面以admin形式呈现(需要在请求地址后缀添加?fromat ...

  4. 「Python」字符串操作内置函数

    目录: capitalize casefold center count encode decode endswith expandtabs find format format_map index ...

  5. Asp.net mvc 大文件上传 断点续传 进度条

    概述 项目中需要一个上传200M-500M的文件大小的功能,需要断点续传.上传性能稳定.突破asp.net上传限制.一开始看到51CTO上的这篇文章,此方法确实很不错,能够稳定的上传大文件,http: ...

  6. jdbc:oracle:thin:@localhost:1521:orcl和jdbc:oracle:thin:@localhost:1521/orcl的区别

      Oracle Thin JDBC Driver 驱动程序包名:ojdbc14.jar.ojdbc6.jar 驱动程序类名: oracle.jdbc.driver.OracleDriver JDBC ...

  7. 【BZOJ】2440: [中山市选2011]完全平方数

    [题意]T次询问第k小的非完全平方数倍数的数.T<=50,k<=10^9.(即无平方因子数——素因数指数皆为0或1的数) [算法]数论(莫比乌斯函数) [题解]考虑二分,转化为询问[1,x ...

  8. js中字符串和json数组的相互转换

    //示例 var a={"name":"tom","sex":"男","age":"24& ...

  9. NYOJ 1022 合纵连横 (并查集)

    题目链接 描述 乱世天下,诸侯割据.每个诸侯王都有一片自己的领土.但是不是所有的诸侯王都是安分守己的,实力强大的诸侯国会设法吞并那些实力弱的,让自己的领土面积不断扩大.而实力弱的诸侯王为了不让自己的领 ...

  10. LOW逼三人组(一)----冒泡算法

    排序 1.冒泡排序 冒泡算法 import random # 随机模块 def bubble_sort(li): ###################################冒泡排序#### ...