Codeforces Round #657 (Div. 2)补题

被!教!育!了!

要学习如何分析问题,仔细分析哪些部分可以优化哪些不可以,可能直觉上不可行的算法却有着相当优秀的复杂度。

A. Acacius and String(1500)

题意

给你一个由小写字母和?组成的字符串,你可以将?替换为任意字母。问能否使abacaba恰好出现一次。

思路

很容易拐到KMP上去,因为长度小于50,测试小于5000组,可以考虑 O ( n 2 ) O(n^2) O(n2)的做法。
用KMP统计原串中abacaba的数量,若大于一个则不可能,如果等于一个,则将所有?替换为不相关的字母即可。
如果等于0,则枚举起点进行暴力check,看看能否修改出来一个abacaba,注意类似abacab?bacaba的情况,所以要将修改后的字符串再跑一次KMP来统计数量是否恰好等于1。

代码
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=105,inf=0x3f3f3f3f,mod=1000000007;
char w[1005]="abacaba",s[10005],ss[maxn];
int nex[1005];//nex[i]记录w[0:i]串的最大公共串长度
void getnext(char *B,int n)
{
	nex[0]=0;
	for(int i=1,j=0;i<n;i++)/* n为B串长度 */
	{
//		j=nex[i-1];j为待计算位前一位对应的最大公共串的下一位(或前一位对应的最大公共串长度)
		while(B[j]!=B[i]&&j>0)//若匹配不上,尝试缩小公共子串,因为B串i之前的j个字符(B[i-j]~B[i-1])与开头的j个(B[0]~B[j-1])相同,直接在前面j个字符里找
			j=nex[j-1];//找最大公共子串的下一位与B[i]相同的最大公共子串,不能用j--,保证B[0]到B[i-1]这段目前匹配前缀后缀始终相同
		if(B[j]==B[i])//公共串下一位字符相同,公共最大长+1
			j++;
		nex[i]=j;//j为0~i最大公共串长度,若最终B[j]!=B[i],j=0
	}
}//https://www.cnblogs.com/c-cloud/p/3224788.html
int kmp(int m,int n)//m为A串长度,n为B串长度
{
	int ans=0,cmp=0;
	for(int i=0;i<m;i++){
		while(cmp>0&&s[i]!=w[cmp])//第cmp个位置匹配失败
			cmp=nex[cmp-1];//下一轮从匹配成功串的最大公共串的下一位开始匹配
		if(s[i]==w[cmp])//当前字符匹配成功,继续下一位
			cmp++;
		if(cmp==n){//子串匹配成功
			ans++;
			cmp=nex[cmp-1];//本题母串分割各部分间可以有公共部分
		}
	}
	return ans;
}
bool check(int k)
{
	bool ok=1;
	for(int i=0;i<7;i++)
		if(s[k+i]!=w[i]&&s[k+i]!='?')
			ok=0;
	if(ok)
		for(int i=0;i<7;i++)
			s[k+i]=w[i];
	return ok;
}
signed main()
{
	int t,n;
	getnext(w,7);
//	string s;
	cin>>t;
	while(t--)
	{
		bool ok=0;
		cin>>n>>ss;
		strcpy(s,ss);
		int tot=kmp(n,7);
		if(tot==1)
		{
			cout<<"YES"<<endl;
			for(int i=0;i<n;i++)
				cout<<(s[i]!='?'?s[i]:'d');
			cout<<endl;
		}
		else if(tot>1)
			cout<<"NO"<<endl;
		else{
			for(int i=0;i<=n-7;i++)
			{
				strcpy(s,ss);
				if(check(i)&&kmp(n,7)==1)
				{
					ok=1;
					cout<<"YES"<<endl;
					for(int i=0;i<n;i++)
						cout<<(s[i]!='?'?s[i]:'d');
					cout<<endl;
					break;
				}
			}
			if(!ok)
				cout<<"NO"<<endl;
		}
	}
	return 0;
}

B.Dubious Cyrpto

题意

m = n × a + b − c m=n\times a+b-c m=n×a+bc l ≤ a , b , c ≤ r l\le a,b,c \le r la,b,cr,现在给出 l , r , m l,r,m l,r,m,请确定一组 a , b , c a,b,c a,b,c使得存在一个 n > 0 n>0 n>0的解。

思路

本场最简单的题, n = m − b + c a n=\frac{m-b+c}{a} n=amb+c,符合要求时 m − b + c m-b+c mb+c一定被 a a a整除。可以使 m m m加上或减去一个数,使它能被 a a a整除,可以发现这个数只和 c − b c-b cb差值有关。所以我们可以枚举 a a a,同时 O ( 1 ) O(1) O(1)枚举此时的 b b b c c c,并尽量让他们落在 [ l , r ] [l,r] [l,r]内。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
signed main()
{
	ll t,l,r,m;
	cin>>t;
	while(t--)
	{
		cin>>l>>r>>m;
		int a,b,c;
		for(a=l;a<=r;a++)
		{
			int res=m%a;
			b=l+res,c=l;
			if(b<=r&&m>a)
				break;
			if(res>0)
				res=a-res;
			b=l,c=l+res;
			if(c<=r)
				break;
		}
		cout<<a<<' '<<b<<' '<<c<<endl;
	}
	return 0;
}

C.Choosing flowers

题意

一个卖花的商店有 m m m种花,每一种买第一朵时有 a i a_i ai点贡献,之后每买一朵有 b i b_i bi的贡献,求买 n n n朵的最大贡献。

思路

可以发现如果取了一个 b b b,则之后只会不断取这个 b b b,因为取 b b b就是因为没有更大的 a a a可取了,同时有多个 b i b_i bi可以取的话我们毫无疑问会取最大的那个。
但是只记录最大的 b b b和其对应的 a a a并不一定能保证得到最优解。考虑枚举不断选择的 b i b_i bi,每次将大于 b i b_i bi a a a取完,再来取 a i a_i ai b i b_i bi
可以用前缀和+二分来快速得到大于 b i b_i bi a a a的和,复杂度 O ( m l o g m ) O(mlogm) O(mlogm)

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=1e5+10,inf=0x3f3f3f3f,mod=1000000007;
struct node
{
	int a,b;
	bool vis;
	node(){}
	node(int a,int b,bool v=0):
		a(a),b(b),vis(v){}
//	friend bool operator<(const node &x,const node &y)
//    {//排在后面
//		return ((x.vis)?x.b:x.a)<((y.vis)?y.b:y.a);
//	}
} arr[maxn];
ll sum[maxn];
signed main()
{
	int t,a,b,n,m;
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
//		priority_queue<node>q;
		for(int i=1;i<=m;i++)
		{
			cin>>a>>b;
			arr[i].a=a,arr[i].b=b;
//			q.push({a,b,0});
		}
		vector<ll>tmp(m);
		for(int i=0;i<m;i++)
			tmp[i]=arr[i+1].a;
		sort(tmp.begin(),tmp.end());
		for(int i=1;i<=m;i++)
			sum[i]=sum[i-1]+tmp[i-1];
//		sort(arr+1,arr+m+1,[](const node &x,const node &y){
//				return x.a>y.a;
//			});
		ll ans=0;
		for(int i=1;i<=m;i++)
		{
			ll cnt=m-(lower_bound(tmp.begin(),tmp.end(),arr[i].b)-tmp.begin());
			ll now=sum[m]-sum[m-cnt];
			if(cnt>=n)
			{
				ans=max(ans,sum[m]-sum[m-n]);//选最大的n个a
				continue;
			}
//			for(int j=1;j<=m&&j<=n&&arr[j].a>=arr[i].b;j++)
//				now+=arr[j].a,cnt++;
			if(arr[i].a<arr[i].b)//未选到i
				now+=arr[i].b*(n-cnt-1)+arr[i].a;
			else
				now+=arr[i].b*(n-cnt);
//			printf("cnt=%lld,now=%lld\n",cnt,now);
			ans=max(ans,now);
		}
		cout<<ans<<endl;
	}
	return 0;
}
/*
可以发现只能取一种b
思路一:优先队列大模拟,不能保证最优,存在b很大但a很小的情况
只记录最大b也是不行的,同样不能保证最优
思路二:枚举每一个b,或者不选b,则强行选择这个b+a和大于他的a
复杂度m^2
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值