zoukankan      html  css  js  c++  java
  • 【LOJ】#2012. 「SCOI2016」背单词

    题解

    我们发现第一种操作肯定不可取,每个节点里它最近的点是它最长出现过的后缀,发现这就是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
    ",ans);
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
    }
    
  • 相关阅读:
    基础档案后台CO应用实例_存货档案自动同步功能
    U8应收管理Co单据
    U8供应链各业务单据CO功能
    jq判断是PC还是手机端的方法
    C#下OCX控件的完美使用
    如何使用C#调用U8的COM组件之四 Interop合并方案
    如何使用C#调用U8的COM组件之三 繁多的Interop
    如何使用C#调用U8的COM组件之二 利器与初探
    如何使用C#调用U8的COM组件之 一前言
    【vue】table动态加载图片时,遇到图片不显示问题
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9155856.html
Copyright © 2011-2022 走看看