zoukankan      html  css  js  c++  java
  • 2018ECfinal J. Philosophical Balance

    2018ECfinal J. Philosophical Balance

    题目大意:

    给出一个字符串 (s) ,你需要给每一个 (i) 一个 ([0,1]) 之间的权值 (k_i) ,且满足 (sum k_i=1) 。并且最小化

    [max_{i=1}^n(sum_{j=1}^nlcp(suf(s,j),suf(s,i)) imes k_j) ]

    先证明一个比较简单的结论,把 (s) 的后缀树建出来,考虑后缀树的每一个子树,如果子树的根是 (s) 的一个后缀,那么分配到这个子树里面的所有权值必然全部分配到根上。

    假设这个子树 (u) 里第 (i) 个节点分配了 (k_i) 的权值,那么对于根的贡献是 (sum{k_i} imes len_{u}) ,考虑对其它点的贡献是每一个权值乘在其某个祖先的 (len) 上,由于 (len) 向上递减,根的贡献固定,那么显然全部放在根上最优。

    (f(u)) 为后缀树上 (u) 节点所在子树内部的答案,当 (u) 是后缀节点的时候 (f(u)=len(fa_u)-len(u)) 。否则只需要合并每一个儿子的 (f(v))

    考虑儿子与儿子之间是独立的,不同儿子之间的 (lcp) 都是 (u) ,也就是说 (u) 代表的串无论 (i) 是什么都会被算 (sum k_i=1) 的系数。那么 (u) 到父亲的边直接加上去就可以。然后剩下的东西是一个纳什均衡的模型,可以当做博弈来理解,最优解的一定是每个儿子的贡献相等,乱推一波可以得到式子:

    [sum {k_i} = 1 \ f_1k_1 = f_2k_2=dots= f_mk_m= x \ sum frac{x}{f_i}=1 \ frac{1}{x} = sum frac{1}{f_i} \ x = frac{1}{sum frac{1}{f_i}} ]

    其实上面那个证明带进去发现当根节点是后缀节点的时候贡献是相等的,打表一下应该不难看出这个后面那个结论了,之后 (mathcal O(n)) 算一算就做完了。

    /*program by mangoyang*/
    #pragma GCC optimize("Ofast", "inline")
    #include <bits/stdc++.h>
    #define inf (0x7f7f7f7f)
    #define Max(a, b) ((a) > (b) ? (a) : (b))
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    typedef long long ll;
    using namespace std;
    template <class T>
    inline void read(T &x){
    	int ch = 0, f = 0; x = 0;
    	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    	if(f) x = -x;
    }
    #define double long double
    const int N = 500005;
    vector<int> vec[N];
    char s[N];
    ll all, val[N]; int n, cnt;
    namespace SAM{
    	vector<int> g[N];
    	int ch[N][26], pos[N], fa[N], len[N], size = 1, tail = 1;
    	inline int newnode(int x){ return len[++size] = x, size; }
    	inline void ins(int c, int x){
    		int p = tail, np = newnode(len[p] + 1); pos[np] = x;
    		for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
    		if(!p) return (void) (fa[np] = 1, tail = np);
    		int q = ch[p][c];
    		if(len[q] == len[p] + 1) fa[np] = q;
    		else{
    			int nq = newnode(len[p] + 1);
    			fa[nq] = fa[q], fa[q] = fa[np] = nq;
    			for(int i = 0; i < 26; i++) ch[nq][i] = ch[q][i];
    			for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
    		}tail = np;
    	}
    	inline void addedge(){
    		for(int i = 2; i <= size; i++) g[fa[i]].push_back(i);
    	}
    	inline double gao(int u, int d){
    		if(pos[u]) return d;
    		double res = 0;
    		for(int i = 0; i < (int) g[u].size(); i++)
    			res += 1.0 / gao(g[u][i], len[g[u][i]] - len[u]);
    		return 1.0 / res + d;
    	}
    }
    int main(){
    	int T; read(T);
    	while(T--){
    		for(int i = 1; i <= SAM::size; i++){
    			SAM::g[i].clear();
    			SAM::pos[i] = SAM::fa[i] = SAM::len[i] = 0;
    			memset(SAM::ch[i], 0, sizeof(SAM::ch[i]));
    		}
    		SAM::tail = SAM::size = 1;
    		scanf("%s", s + 1); n = strlen(s + 1);
    		for(int i = n; i; i--) SAM::ins(s[i] - 'a', i);
    		SAM::addedge();
    		printf("%.10Lf
    ", SAM::gao(1, 0));
    	}
    }
    
  • 相关阅读:
    两道简单的机试题目
    C#二维数组(矩形数组,交错数组)
    用实例展示left Join,right join,inner join,join,cross join,union 的区别
    React 中的 Redux 详解:
    react 组件通信:
    vue 入门, todoList
    Vue 动画
    vue 框架,入门必看
    koa2 的使用方法:(一)
    git 的使用:
  • 原文地址:https://www.cnblogs.com/mangoyang/p/10476902.html
Copyright © 2011-2022 走看看