• 【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();
    }
    
  • 相关阅读:
    Get-CrmSetting返回Unable to connect to the remote server的解决办法
    Dynamics 365中的常用Associate和Disassociate消息汇总
    Dynamics 365 Customer Engagement V9 活动源功能报错的解决方法
    Dynamics Customer Engagement V9版本配置面向Internet的部署时候下一步按钮不可点击的解决办法
    Dynamics 365检查工作流、SDK插件步骤是否选中运行成功后自动删除系统作业记录
    注意,更改团队所属业务部门用Update消息无效!
    Dynamics 365的审核日志分区删除超时报错怎么办?
    Dynamics 365使用Execute Multiple Request删除系统作业实体记录
    Dynamics 365的系统作业实体记录增长太快怎么回事?
    Dynamics CRM日期字段查询使用时分秒的方法
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9155856.html
Copyright © 2020-2023  润新知