CF-943(已更B-E,G1)

CF- 943(已更 B-E,G1)

D赛时没调出来(╬▔皿▔)╯,还有几分钟的时候反而把E过了,本来应该是上大分的一场(⊙﹏⊙)

这假期要刷题,还要补文化课……后面有空的话更一下之前打的线下赛的题解

B

双指针……

void solve(){
	int n,m;cin>>n>>m;
	string a,b;cin>>a>>b;
	int now=0,ans=0;
	rep(i,0,n-1){
		while(a[i]!=b[now]&&now<m) now++;
		if(a[i]==b[now]){//找到的话快指针now移动,合法长度ans++
			now++;
			ans++;
		}
		if(now==m){//找不到就break
			break;
		}
	}
	cout<<ans<<endl;
 

C

用到了一点数学知识

分析

已知 a[i]%a[i-1]=x[i]

则有 (a[i]-x[i])|a[i-1]

|表示能整除

所以 a[i]=k*a[i-1]+x[i]

但同时必有x[i]<a[i-1],由此可以得到k的取值

代码

void solve(){
	int n;cin>>n;
	rep(i,2,n){
		cin>>x[i];
	}
	a[1]=x[2]+1;//由样例可知
	rep(i,2,n){
		int k=1;
		if(i==n){
			a[i]=a[i-1]+x[i];
			continue;
		}
		while(a[i]<=x[i+1]){
			a[i]=k*a[i-1]+x[i];
			k++;
		}
	}
	rep(i,1,n){
		cout<<a[i]<<" ";
	}
	cout<<endl;
	rep(i,1,n+1){
		x[i]=a[i]=0;
	}
}

D

考察了循环结构、顺序结构……反正我赛时是因为这个写假了

分析

暴力枚举两人会在哪个点开始一直停留,设这个点是now,此前已移动了pre次,那么之后对答案的贡献就是$a[now]*(k-pre)$,而此前移动对答案的贡献res我们可以每次移动时就更新一次,我们对其取max就是两人的最大得分

比如10 8 2 10
3 1 4 5 2 7 8 10 6 9
5 10 5 1 3 7 10 15 4 3
对于后手:
一开始now=10,pre=0,res=0,若在该点一直停留对答案的贡献为a[10]*8=24;
此后:
now=9,pre=1,res=3————3+a[9]*7=31
now=6,pre=2,res=7————7+a[6]*6=49
				 ————14+a[7]*5=64
				 ————24*a[8]*4=84
now=10,break;

正解代码

void solve(){
	int n,k,ps,pb;cin>>n>>k>>pb>>ps;
	rep(i,1,n) cin>>p[i];
	rep(i,1,n) cin>>a[i];
	int aa=0,bb=0;
	if(p[pb]==pb) aa=k*a[pb];
	if(p[ps]==ps) bb=k*a[ps];
	int now=pb,pre=0,res=0;
	while(1){
		if(pre<=k){
			aa=max(aa,res+a[now]*(k-pre));
		}
		res+=a[now];
		now=p[now];
		if(now==pb) break;
		pre++;
	}
	now=ps,pre=0,res=0;
	while(1){	
		if(pre<=k){
			bb=max(bb,res+a[now]*(k-pre));
		}
		res+=a[now];
		now=p[now];
		if(now==ps) break;
		pre++;
	}
	if(aa>bb){
		cout<<"Bodya";
	}
	else if(aa<bb){
		cout<<"Sasha";
	}
	else{
		cout<<"Draw";
	}
	cout<<endl;

贴一个赛时样例都过不了的假写法,虽然思路是一样的(╬▔皿▔)╯

int now=pb,pre=0,res=0;
	//cout<<now<<" "<<p[now]<<endl;
	while(p[now]!=pb){//实际上p[now]=pb就跳出了
		//cout<<now<<" "<<p[now]<<" "<<res<<endl;
		if(pre<=k){
			aa=max(aa,res+a[now]*(k-pre));
		}
		//else break; 
		//cout<<aa<<" "<<" "<<pre<<" "<<now<<" "<<res<<endl;
		res+=a[now];
		now=p[now];
		pre++;
	}
	now=ps,pre=0,res=0;
	while(p[now]!=ps){
		if(pre<=k){
			bb=max(bb,res+a[now]*(k-pre));
		}
		//else break;
		//cout<<bb<<" "<<" "<<pre<<" "<<now<<" "<<res<<endl;
		res+=a[now];
		now=p[now];
		pre++;
	}
	cout<<aa<<" "<<bb<<endl;

E

万恶的构造题

void solve(){
	int n;cin>>n;
	if(n==2){
		cout<<"1 1"<<endl<<"2 2"<<endl<<endl;
		return;
	} 
	rep(i,1,n-2){
		cout<<"1 "<<i<<endl;
	}
	cout<<n-1<<" 1"<<endl;
	cout<<n<<" "<<n<<endl<<endl;
}

G1

这题磨了好久,因为自己之前不会找LCP,check里写了个自以为正确的n方的做法……后来问人才知道有Z函数可以o1查询LCP

缝了大佬的Z函数板子,自己也只会用,原理都还不懂……

分析

此题可供二分的单调性在于:最长公共前缀越长,可分的段数越少,于是所求答案就是可分段数为l或r的LCP的最大值,这个最大值可以二分求得

代码

string s;
const int N=3e5+5;
int n,x;
int z[N];
bool ck(int mid){
	int cnt=0;//可分的段数
	rep(i,1,n){//z[i]表示原串从i开始的后缀的最长的lcp
		if(z[i]>=mid){
			i+=mid-1;
			cnt++;
		}
	}
	//cout<<mid<<" "<<cnt<<endl;
	if(cnt>=x) return true;
	else return false; 
}
void solve(){
	cin>>n>>x>>x;
	cin>>s;
	s=' '+s;
	z[1]=n;
	for(int i=2,l=1,r=1;i<=n;i++){
		z[i]=(i<=r?min(z[i-l+1],r-i+1):0);
		while(i+z[i]<=n&&s[i+z[i]]==s[1+z[i]]) z[i]++;
		if(i+z[i]-1>r) r=i+z[i]-1,l=i;
		//cout<<l<<" "<<r<<" "<<i<<z[i]<<endl;
	}
	int l=0,r=n/x+1,mid;//二分的
	while(r-l>1){
		mid=l+r>>1;
		if(ck(mid)) l=mid;
		else r=mid;
	}
	cout<<l<<endl;
}
posted @ 2024-05-03 12:15  mono_4  阅读(34)  评论(0编辑  收藏  举报