1. 程式人生 > 實用技巧 >CF Good Bye 2020 題解&總結 A~G

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題標頭檔案沒有寫全了。