• 【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();
    }
    
  • 相关阅读:
    Thinhole类声明和实现
    Pinhole类声明和实现
    Camera类定义和实现
    同心映射定义和测试
    Hammersley采样类定义和测试
    NRooks采样类定义和测试
    MultiJittered采样类定义和测试
    Jittered采样类定义和测试
    git分支操作
    Git单人本地仓库操作
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9155856.html
Copyright © 2020-2023  润新知