zoukankan      html  css  js  c++  java
  • 题解 【CF1286E Fedya the Potter Strikes Back】

    (Large exttt{CF1286E Fedya the Potter Strikes Back })

    期望理论复杂度是 (mathcal O(nalpha(n))),理论上是吊打了表算,但是好像实际并不优于一些 (mathcal O(nlog n)) 的提交。

    思路

    对于我们需要动态维护的这个答案,先做个归类,将所有有贡献的区间 ([L, R]),令其为点 (R) 的贡献,发现很难每次直接对点 (i) 算出其贡献,考虑继承 (i - 1) 的贡献。

    为了方便理解,这里引入后缀树的概念。

    对于点 (i - 1)(i),它们在后缀树上的深度一定满足 (dep_{i} le dep_{i - 1} + 1),也就是说每次添加一个字符最多只会增加一个 border,且其长度为 (1)

    那么我们考虑如何继承 (i - 1) 的贡献到 (i),对于点 (i) 在后缀树上到根节点上的所有点都一定是点 (i - 1) 在后缀树上的到根节点上的所有点继承而来(除了长为 (1) 的border), 也就是说,我们将点 (i) 有贡献的区间 ([L, i]) 中的所有左端点归为集合 (S_i),那么 (S_i in S_{i - 1} cup {i}),因此我们只要暴力删除所有不满足的 (L) 并在 (S_{i - 1}) 之中,其它直接继承即可,删除总数最大为 (mathcal O(n))

    我们用单调栈维护后缀递增的 (w_i),因为只有这些元素是有贡献的,并在此过程中将当前所有 (L in S_i) 插入其中属于其贡献的位置 (w_i),对于插入新的元素 (w_i),若需要合并删去栈顶元素,那么用并查集将其指向 (i) 即可,也就是将原本贡献在栈顶的元素移到 (i)

    对于删除操作和其它题解差不多,维护一个点在后缀树上第一个后继字符和自己后继字符不一样的祖先,暴力跳并删除即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    #define PB push_back
    // #define int long long
    #define LL long long
    #define siz(a) ((int)((a).size()))
    #define rep(i, a, b) for (int i = (a); i <= (b); ++i)
    #define per(i, a, b) for (int i = (a); i >= (b); --i)
    const int N = 6e5;
    const int M = 100000;
    const int mod = 1e9 + 7;
    const int inf = 1e9;
    
    int a, nxt[N + 5], w[N + 5], fa[N + 5], top, siz[N + 5], sk[N + 5], lst[N + 5], sz[N + 5];
    __int128 ans, tmp;
    char s[N + 5];
    
    int f(int n) {
    	return fa[n] == n ? n : fa[n] = f(fa[n]);
    }
    
    void merge(int &p1, int &p2) {
    	tmp -= 1ll * w[p2] * siz[p2];
    	tmp += 1ll * w[p1] * siz[p2];
    	if(sz[p1] < sz[p2]) swap(p1, p2);
    	w[p1] = min(w[p1], w[p2]);
    	fa[p2] = p1;
    	siz[p1] += siz[p2];
    	sz[p1] += sz[p2];
    }
    void out(__int128 x){
    	static int buf[255], len;
    	if(!x) return cout << "0
    ", void();
    	while(x) buf[++len] = x % 10, x /= 10;
    	while(len) cout << (char) (buf[len--] + '0');
    	cout << "
    ";
    }
    
    signed main() {
    	// freopen("in1.in", "r", stdin);
    	ios::sync_with_stdio(0);
    	cin.tie(0);
    	cout.tie(0);
    	cin >> a;
    	int j = 0, mx = (1ll << 30) - 1;
    	rep(i, 1, a) {
    		cin >> s[i] >> w[i];
    		w[i] = (w[i] ^ (ans & mx));
    		s[i] = (ans + s[i] - 'a') % 26 + 'a';
    		sk[++top] = i;
    		fa[i] = i;
    		sz[i] = 1;
    		while(top > 1 && w[sk[top]] <= w[sk[top - 1]]) {
    			swap(sk[top], sk[top - 1]);
    			merge(sk[top - 1], sk[top]);
    			--top;
    		}
    		if(i > 1) {
    			lst[i - 1] = ((s[i] == s[j + 1]) ? lst[j] : j);
    			for(; j && s[j + 1] != s[i]; j = nxt[j]) {
    				--siz[f(i - j)];
    				tmp -= w[f(i - j)];
    			}
    			for(int k = lst[j]; k; ) {
    				if(s[k + 1] == s[i]) {
    					k = lst[k];
    					continue;
    				}
    				--siz[f(i - k)];
    				tmp -= w[f(i - k)];
    				k = nxt[k];
    			}
    			if(s[j + 1] == s[i]) ++j;
    			nxt[i] = j;
    			if(s[1] == s[i]) {
    				++siz[f(i)];
    				tmp += w[f(i)];
    			}
    		}
    		ans += tmp + w[f(sk[1])];
    		out(ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    2014年10月10号——数据类型之间的转换
    2014年10月9日——语言基础2
    2014年10月9日——语言基础1
    2014年10月8日——进制转换
    js
    SQL的数据类型
    SQL,学习基础2
    SQL数据库的备份和恢复
    用java调用oracle存储过程总结(转)
    用JAVA调用Mysql数据库,数据存储过程的封装与调用;
  • 原文地址:https://www.cnblogs.com/RedreamMer/p/15518568.html
Copyright © 2011-2022 走看看