首页 > 其他分享 >CF610E

CF610E

时间:2024-01-16 17:15:15浏览次数:28  
标签:node int pos st -- CF610E itr

简单题。想到怎么计数就结束了。

重点就是怎么样计算循环次数。肯定是不能枚举一遍,双指针去数的。

但是发现 \(t\) 有一个很好的性质:它是 \([1,k]\) 内字符的排列。说明每个字符在 \(t\) 中只会出现一次。然后发现,可以按照最长公共子序列那题类似的思路,根据 \(t\) 内字符的位置为权值,求上升子段个数,这和循环次数是等价的。

而这个东西就很好求了,可以记录 \(\forall x,y,s_i=x,s_y=i\) 的个数,然后每次枚举 \(x,y\),看权值大小关系算。

还要求区间推平,这种上颜色段均摊显然是很合适的。复杂度大概是 \(O(m\log n+k^2m)\) 的,好像优于其他题解(可能有误,请指出)?目前只略逊于一个 zkw 线段树。优势是思路清晰,不容易写挂。

code:

点击查看代码
int n,m,k,c[13][13];
char s[N];
struct CHtree{
	struct node{
		int l,r,k;
		node(int _l=0,int _r=0,int _k=0):l(_l),r(_r),k(_k){}
		bool operator<(const node &rhs)const{return l<rhs.l;}
	};
	set<node> st;
	void init(){
		rep(i,1,n)st.insert(node(i,i,s[i]-'a'));
	}
	il auto split(int pos){
		if(pos==n+1)return st.end();
		auto it=st.lower_bound(node(pos,0,0));
		if(it!=st.end()&&it->l==pos)return it;
		node tmp=*--it;
		st.erase(it);
		st.insert(node(tmp.l,pos-1,tmp.k));
		return st.insert(node(pos,tmp.r,tmp.k)).fi;
	}
	il void assign(int l,int r,int k){
		auto itr=split(r+1),itl=split(l);
		for(auto it=itl;it!=itr;it++){
			c[it->k][it->k]-=(it->r)-(it->l);
			if(it!=st.begin())c[prev(it)->k][it->k]--;
		}
		if(itr!=st.end())c[prev(itr)->k][itr->k]--;
		st.erase(itl,itr);
		auto it=st.insert(node(l,r,k)).fi;
		c[k][k]+=r-l;
		if(it!=st.begin())c[prev(it)->k][k]++;
		if(it!=--st.end())c[k][next(it)->k]++;
	}
}C;
void Yorushika(){
	scanf("%d%d%d%s",&n,&m,&k,s+1);
	C.init();
	rep(i,2,n)c[s[i-1]-'a'][s[i]-'a']++;
	while(m--){
		int op=read(),x,y;char t[13];
		if(op==1){
			scanf("%d%d%s",&x,&y,t);
			C.assign(x,y,t[0]-'a');
		}else{
			scanf("%s",t);
			int ans=0;
			rep(i,0,k-1)rep(j,0,i)ans+=c[t[i]-'a'][t[j]-'a'];
			printf("%d\n",ans+1);
		}
	}
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}

标签:node,int,pos,st,--,CF610E,itr
From: https://www.cnblogs.com/yinhee/p/17968091/CF610E

相关文章