• 【CF1286F】 Fedya the Potter Strikes Back


    题目

    题意:对于一个字符串(S),对于某个子串(S[l,r]),如(S[l,r]=S[1,r-l+1]),那么就称该子串为好的;给定序列(w_i),定义一个子串(S[l,r])的权值为(min_{i=l}^r w_i)

    初始时有一空串,每次在该串末位加入一个字符(c_i),求每次操作后字符串所有好的子串的权值和;强制在线。

    (nleq 6 imes 10^5,w_i<2^{30})

    不难发现这个好的子串其实就是( m border),这个动态加入我们只需要考虑答案的增量即可。

    考虑从(i-1)加一个字符(c_i)之后( m border)的变化,如果(x)(S[1,i-1])是一个( m border),那么如果(S_{x+1}=c_i),那么(x+1)就是(S[1,i])( m border),反之则不是;特殊地,当(S_1=c_i)时,(1)会成为一个( m border)

    我们发现这个变化比较简单,于是我们只需要考虑( m border)集合的变化即可。

    加入新的( m border)特判一下即可;考虑如何删掉加入(c_i)后不合法的( m border),注意到(x)不合法当且仅当(S_{x+1} eq c_i),于是我们记(x)的颜色为(S_{x+1}),我们利用kmp构建一棵fali树,对于每一种颜色分别维护每个节点往上第一个该颜色的祖先;这样我们枚举(x)的颜色,从(i-1)暴力跳着删除即可;删除的时候贡献是一个区间( m min),对(w_i)动态构建一个st表即可;由于每个点只会被删除一次,所以复杂度是正确的。

    之后对于合法的( m border),其只新增了一个(w_i),也就是贡献需要对(w_i)取一个(min),于是我们需要一个数据结构支持整体求和、将所有数和(w_i)(min)、插入以及删除,刚上来写了个权值线段树发现被卡空间了,真实自闭;其实只需要一个std::map暴力修改即可,由于一次插入的数之后被暴力取(min)一次,复杂度也是均摊正确的。

    之后答案可能是(n^2 imes w_i)级别,于是可能会爆long long,所以统计答案的时候需要一个高精。

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const int maxn=600001;const LL B=(1<<30)-1;
    char c[5],S[maxn];
    int A[105],C[105],ans26,ansB,ln;
    int n,w[maxn],mn[20][maxn],nxt[maxn],fa[26][maxn],lm;
    short col[maxn],lg[maxn];
    inline void add(LL x) {
    	ans26=(ans26+(x%26ll))%26;ansB=(ansB+(x&B))&B;
    	ln=0;while(x)C[++ln]=x%10,x/=10ll;lm=max(lm,ln);
    	for(re int i=1;i<=lm;i++)A[i]+=C[i],C[i]=0;
    	for(re int i=1;i<=lm;i++)A[i+1]+=A[i]/10,A[i]%=10;
    	while(A[lm+1]) A[lm+1]+=A[lm]/10,A[lm]%=10;++lm;
    }
    inline void write() {
    	for(re int i=lm;i;--i)putchar(A[i]+'0');puts("");
    }
    inline int ask(int l,int r) {
    	int k=lg[r-l+1];
    	return min(mn[k][r],mn[k][l+(1<<k)-1]);
    }
    inline void add_St(int pos) {
    	mn[0][pos]=w[pos];
    	for(re int i=1;(1<<i)<=pos;i++) 
    		mn[i][pos]=min(mn[i-1][pos],mn[i-1][pos-(1<<(i-1))]);
    }
    struct Data_Structure {
    	typedef std::pair<int,int> pii;
    	long long tot;std::map<int,int>ma;int st[maxn],top;
    	std::map<int,int>::iterator it;
    	inline void ins(int x,int v) {ma[x]+=v;tot+=1ll*x*v;}
    	inline int qry(int x) {
    		int sz=0;it=ma.upper_bound(x);
    		for(;it!=ma.end();++it) {
    			pii nw=*it;st[++top]=nw.first;
    			tot-=1ll*nw.first*nw.second;sz+=nw.second;
    		}
    		while(top)ma.erase(st[top--]);return sz;
    	}
    }seg;
    inline void jump(int co,int x,int pos) {
    	while(x)seg.ins(ask(pos-x+1,pos),-1),x=fa[co][x];
    }
    int main() {
    	n=read();lm=1;for(re int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
    	for(re int l=1;l<=n;l++) {
    		scanf("%s",c);w[l]=read();
    		c[0]=(c[0]-'a'+(int)(ans26%26))%26+'a';w[l]^=(ansB&B);
    		add_St(l);S[l]=c[0];add(ask(1,l));
    		if(l==1) {write();continue;}
    		int p=nxt[l-1];
    		while(p&&S[p+1]!=S[l])p=nxt[p];
    		if(S[p+1]==S[l])nxt[l]=p+1;
    		col[l-1]=c[0]-'a';
    		for(re int i=0;i<26;++i)fa[i][l]=fa[i][nxt[l]];
    		fa[col[nxt[l]]][l]=nxt[l];
    		if(S[l]==S[1]) seg.ins(w[l],1);
    		for(re int i=0;i<26;++i) {
    			if(c[0]-'a'==i)continue;
    			jump(i,fa[i][l-1],l-1);
    		}
    		seg.ins(w[l],seg.qry(w[l]));
    		add(seg.tot);write();
    	}
    	return 0;
    }
    
  • 相关阅读:
    c#中==和equals的比较
    原型指向改变如何添加方法和访问
    把随机数对象暴露给window成为全局变量
    内置对象Array的原型对象中添加方法
    构造函数可以实例化对象
    原型
    无刷新评论
    大量字符串拼接案例
    元素隐藏占位与不占位
    导航栏切换效果案例
  • 原文地址:https://www.cnblogs.com/asuldb/p/12261061.html
Copyright © 2020-2023  润新知