CF Good Bye 2020 题解&总结 A~G

众所周知这种手速场是掉分好时机。

前200人均切7题,由于我没有切G导致400+。

不过竟然没有掉分真是令人震惊呢。

血的教训:

以后都用万能头文件。


A

求两两之间的差不重复的有多少个。

范围小,暴力即可。


B

比较显然的贪心:丢进桶中,从大往小做。如果当前位置大于等于\(2\),后面位置等于\(0\),就分一个到后面去。


C

卡题了。虽然切了但是血亏。

发现\(O(26^2n)\)的DP会MLE,所以把状态改成:\(f_{i,0/1,0/1}\),后两个状态表示后面两个位置是否被改过。

如果两个位置都改过了,那么可以认为两个位置不同。因为字符集大小比较大,所以肯定存在方案调整使得两个位置不同,并且满足其它的不等关系。


D

显然每个联通块一定是连续的;否则可以只保留那个比较大的连通块。

考虑一个点\(x\)的贡献为它所属的不同连通块个数。显然最多属于\(deg_x\)个。

可以发现把一个连通块分成两个连通块,交界的点只有一个。

增量法搞。一开始只有一个连通块,将所有\(deg_x\)减一。每次找到\(deg_x\)不为\(0\)的权值最大的点\(x\),将其权值加入答案并把\(deg_x\)减一。


E

由于ll事先看了下发现它很水提醒了我们,所以在切A之后立即切了E。

显然可以枚举\(j\),分别算\(i\)\(k\)的贡献和乘起来。

计算贡献的时候可以先预处理对于每一位,这一位上是\(1\)的数有多少个。


F

卡题了,还挂了4次,血亏。

可以抽象成一个图。先不考虑自环,则最终形成的图中,如果出现环,那么环上的一条边可以被其它边替代;所以最终形成的图是个森林。考虑自环,如果一棵树中存在一个自环,那么树中的每个节点都可以任意调整。

按顺序加边。加入一个自环时,如果树中没有自环就加入;加入一条普通边时,如果不在同个连通块,并且不是两个连通块中都有自环,那么就加入。实现的时候搞个超级点\(0\),连自环的时候和\(0\)连边,连普通边的时候直接判断是否在同个连通块中。


G

最上面的那张图已经清楚地表明了一切。

先将字符串扩展成最小的\(s_i\),使刚好满足\(|s_i|\ge |w|\)。然后答案分成两部分:\(s_i\)内部的贡献乘\(2^{k-i}\),和对于所有\(j\in(i,k]\)\(s_it_js_i\)跨过中点的贡献。

前者暴力搞。对于后者,分别找\(lmx,rmx\)\(lmx\)表示最长的\(w\)的前缀匹配\(s_i\)的后缀的长度,即\(w[1..lmx]=s_i[|s_i|-lmx+1..|s_i|]\)\(rmx\)类似。可以强行哈希或exkmp搞出来。

分别处理正反串kmp的数组\(p_i,q_i\),分别以此建树。设\(x\)\(y\)为贡献时的第一个\(s_i\)的后缀和第二个\(s_i\)的前缀的长度,如果\(x+y+1=n\)\(w[x+1]=t[j]\)\(lmx\in subtree_p(x),rmx\in subtree_q(y)\),那么这组\((x,y)\)就可以贡献到。由于询问的\((lmx,rmx)\)只有一个,\(t[j]\)只有\(26\)种不同的取值,直接\(O(|w|)\)处理出来每个取值是什么时的答案。最后统计一下\(t_j\)相同时的贡献之和,随便计算一下即可。

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 1000005
#define ll long long
#define mo 1000000007
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int n,m,Q;
char s0[N],t[N];
char w[N],v[N];
int k,len;
int p[N],q[N];
char s[N*4],s_[N*4];
int ns;
int pw[N*4];
int bf(){
	int res=0;
	for (int i=1,j=0;i<=ns;++i){
		while (j && w[j+1]!=s[i])
			j=p[j];
		if (w[j+1]==s[i]){
			++j;
			if (j==len)
				res++,j=p[j];
		}
	}
	return res;
}
ll ans;
int lmx,rmx;
#define mo2 1000000009
struct hsh{
	int x,y;
};
hsh operator+(hsh a,hsh b){return {(a.x+b.x)%mo,(a.y+b.y)%mo2};}
hsh operator+(hsh a,int b){return {(a.x+b)%mo,(a.y+b)%mo2};}
hsh operator*(hsh a,int b){return {(ll)a.x*b%mo,(ll)a.y*b%mo2};}
bool eql(hsh a,hsh b){return a.x==b.x && a.y==b.y;}
int mxsuc(char w[],char s[]){
	int res=0;
	hsh hs,hw,p;
	hs=hw={0,0};
	p={1,1};
	for (int i=ns,j=1;i>=1 && j<=len;--i,++j){
		hs=hs*26+(s[i]-'a');
		hw=hw+p*(w[j]-'a');
		if (eql(hs,hw))
			res=j;
		p=p*26;
	}
	return res;
}
void init(){
	memcpy(s_,s,sizeof(char)*(ns+1));
	memcpy(v,w,sizeof(char)*(len+1));
	reverse(s_+1,s_+ns+1);
	reverse(v+1,v+len+1);
	q[1]=0;
	for (int i=2,j=0;i<=len;++i){
		while (j && v[j+1]!=v[i])
			j=q[j];
		if (v[j+1]==v[i])
			++j;
		q[i]=j;
	}
	lmx=mxsuc(w,s);
	rmx=mxsuc(v,s_);
}
int anc[N],buc[26];
vector<int> tc[26],ts[26];
int sum[26];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	pw[0]=1;
	for (int i=1;i<=4000000;++i)
		pw[i]=pw[i-1]*2%mo;
	scanf("%d%d%s%s",&n,&Q,s0+1,t+1);
	m=strlen(s0+1);
	for (int i=0;i<26;++i){
		tc[i].push_back(0);
		ts[i].push_back(0);
	}
	for (int i=1;i<=n;++i){
		tc[t[i]-'a'].push_back(i);
		(sum[t[i]-'a']+=qpow(2,mo-1-i))%=mo;
		ts[t[i]-'a'].push_back(sum[t[i]-'a']);
	}
	while (Q--){
		scanf("%d%s",&k,w+1);
		len=strlen(w+1);
		p[1]=0;
		for (int i=2,j=0;i<=len;++i){
			while (j && w[j+1]!=w[i])
				j=p[j];
			if (w[j+1]==w[i])
				++j;
			p[i]=j;
		}
		ns=m;
		for (int i=1;i<=ns;++i)
			s[i]=s0[i];
		ans=0;
		int i=0;
		for (;i<k && ns<len;){
			++i;
			s[ns+1]=t[i];
			for (int i=1;i<=ns;++i)
				s[ns+1+i]=s[i];
			ns=ns*2+1;
		}
		(ans+=(ll)bf()*pw[k-i])%=mo;
		if (i==k){
			printf("%lld\n",ans);
			continue;
		}
		init();
		memset(anc,0,sizeof(int)*(len+1));
		for (int x=rmx;1;x=q[x]){
			anc[x]=1;
			if (x==0)
				break;
		}
		memset(buc,0,sizeof buc);
		for (int x=lmx;1;x=p[x]){
			if (anc[len-1-x])
				buc[w[x+1]-'a']++;
			if (x==0)
				break;
		}
		for (int j=0;j<26;++j){
			int p=upper_bound(tc[j].begin(),tc[j].end(),k)-tc[j].begin()-1;
			int q=upper_bound(tc[j].begin(),tc[j].end(),i)-tc[j].begin()-1;
			(ans+=(ll)(ts[j][p]-ts[j][q])*buc[j]%mo*pw[k])%=mo;
		}
		ans=(ans+mo)%mo;
		printf("%lld\n",ans);
	}
	return 0;
}


H

没看题意。


I

没看题意。


感觉这种比赛一卡题人就没了。

前6题人均AC。唯一有点区分度的就是G题,后面的题都是在神仙打架了。

这次最亏的大概就是F没想好就挂了4次和G题头文件没有写全了。

posted @ 2020-12-31 14:22  jz_597  阅读(270)  评论(0编辑  收藏  举报