Educational Codeforces Round 59 (Rated for Div. 2)题解

Educational Codeforces Round 59 (Rated for Div. 2)题解

http://codeforces.com/contest/1107

A. Digits Sequence Dividing

题目大意

给出一个数字要求将其分成数段,要保证后面的数总是大于前面的数,如不能分则输出NO

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
char num[305];
int main()
{
	int q;
	scanf("%d",&q);
	while(q--)
	{
		int len;
		scanf("%d",&len);
		scanf("%s",num);
		if(len==2)
		{
			if(num[1]<=num[0]) {printf("NO\n");continue;}
		}
		printf("YES\n");
		printf("2\n");
		printf("%c %s\n",num[0],num+1);
	}
}
 

B. Digital root

题目大意

一个数的根数是指其迭代各位相加的数最终成为一位数的数,现要求第k个数其数根为x

AC代码

#include<cstdio>
using namespace std;
int main()
{
	int n;
	scanf("%d",&n);
	while(n--)
	{
		long long k,t;
		scanf("%lld%lld",&k,&t);
		printf("%lld\n",(k-1)*9+t);
	}
}

C. Brutality

题目大意

给出一个字符串,与之相匹配的有一个伤害串,摁下字符串上的字符会造成伤害串上相应位置的伤害,操作者必须按照字符串的顺序进行摁键,但是可以跳过一些字符,问同一个字符被摁不超过k次的情况下,如何打出最高伤害

AC代码

#include<iostream>
#include<cstdio>
#include<queue>
#define int long long 
using namespace std;
priority_queue<int> q;
int arr[200005];
char s[200005];
int32_t main()
{
	 int k,n;
	 scanf("%lld%lld",&k,&n);
	 for(int i=1;i<=k;i++)
	 scanf("%lld",&arr[i]);
	 scanf("%s",s+1);
	 long long ans=0;
	 for(int i=1;i<=k;i++)
	 {
	 	if(i==1||s[i]==s[i-1]) q.push(arr[i]);
	 	else
	 	{
	 		int tot=n;
	 		while(!q.empty())
	 		{
	 			if(tot)
	 			{
	 				ans+=q.top();
	 				tot--;
	 			}
	 			q.pop();
	 		}
	 		q.push(arr[i]);
	 	}
	 }
	 int tot=n;
	 		while(!q.empty())
	 		{
	 			if(tot)
	 			{
	 				ans+=q.top();
	 				tot--;
	 			}
	 			q.pop();
	 		}
	 printf("%lld\n",ans);
}

D. Compression

题目大意

假如一个由16进制代表的2进制矩阵中的每个点都可以通过 A [ i ] [ j ] = B [ [ i x ] ] [ [ i x ] ] A[i][j]=B[[\frac{i}{x}]][[\frac{i}{x}]] A[i][j]=B[[xi]][[xi]]表示,则称这个矩阵A可以被x表示

解题思路

转成2进制矩阵之后找出行和列中所有的连续相同的字符的长度,求一个最大公因数即可

AC代码

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
char m[5210][1310];
bool ma[5210][5210];
bool num[5210];
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%s",m[i]+1);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n/4;j++)
		{
			int v;
			if(m[i][j]>='0'&&m[i][j]<='9') v=m[i][j]-'0';
			else v=m[i][j]-'A'+10;
			for(int k=4;k>=1;k--)
			{
				ma[i][(j-1)*4+k]=v&1;
				v>>=1;
			}
		}
	}
	int col1=0;int col2;
	for(int i=1;i<=n;i++)
	{
		col1=0;col2=0;
		for(int j=1;j<=n;j++)
		{
			if(j==1||ma[i][j]==ma[i][j-1]) col1++;
			else num[col1]=1,col1=1;
			if(j==1||ma[j-1][i]==ma[j][i]) col2++;
			else num[col2]=1,col2=1;
		}
		num[col1]=1,num[col2]=1;
	}
	int ans=-1;
	for(int i=1;i<=5200;i++) 
	{
		if(!num[i]) continue;
		if(ans==-1) ans=i;
		else ans=gcd(ans,i);
	}
	printf("%d\n",ans);
}

E. Vasya and Binary String

题目大意

给出一串01串,相同的字符可以消去,消去后被消去子串两边将重新连接到一起,每消去一个长度x的可以得到一个收益 v x v_x vx问最大可以获得的收益

解题思路

首先预处理出每一个长度的最优消除价值,根据左右端点和剩余的未消去的在相同字符连续区段间跳跃地记忆化搜索.即可得出答案

AC代码

#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
char s[105];
int dp[105][105][105];
int cnt[105];
int tot=0;
int arr[105];
int dfs(int l,int r,int rest)
{
	if(dp[l][r][rest]) return dp[l][r][rest];
	if(l==r) return dp[l][r][rest]=arr[cnt[r]+rest];
	dp[l][r][rest]=dfs(l,r-1,0)+arr[cnt[r]+rest];
	for(int i=r-2;i>=l;i-=2) dp[l][r][rest]=max(dp[l][r][rest],dfs(l,i,rest+cnt[r])+dfs(i+1,r-1,0));
	return dp[l][r][rest];
}
int32_t main()
{
	int n;
	scanf("%lld",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
	for(int i=2;i<=n;i++) for(int j=1;j<i;j++) arr[i]=max(arr[i],arr[i-j]+arr[j]);
	int j,i;
	for(i=1;i<=n;i=j)
	{
		for(j=i;s[i]==s[j];j++);
			cnt[++tot]=j-i; 
	}
	printf("%lld\n",dfs(1,tot,0));
}

F. Vasya and Endless Credits

题目大意

给出n只贷款,每只贷款在贷款初可以得到 a i a_i ai的钱,而在贷款过后接下来的 k i k_i ki个月,每个月需要付出 b i b_i bi的利息,在每个月最多借一种贷款的前提下,问如何借贷可以使得手中持有的钱的峰值最大

解题思路

思路1:对贷款和贷款后到达峰值的时间连边,然后跑出最小费用流即可(时间卡得比较紧,我自己的费用流板子怎么都过不掉但是确实有人是这么过的)

思路2:思路大致上同,不过做二分图匹配

思路3:对每种贷款据其每月还贷的金额从大到小排序,设贷款后到达峰值的时间为x,对x进行dp .对每个i,对其x倒叙更新 d p [ x + 1 ] = m a x ( d p [ x + 1 ] , d p [ x ] + a [ i ] − b [ i ] ∗ x ) dp[x+1]=max(dp[x+1],dp[x]+a[i]-b[i]*x) dp[x+1]=max(dp[x+1],dp[x]+a[i]b[i]x)意为剩余x+1天如借i贷可以使得总的钱数增加则,购买.再更新 d p [ x ] = m a x ( d p [ x ] , d p [ x ] + a [ i ] − b [ i ] ∗ k [ i ] ) dp[x]=max(dp[x],dp[x]+a[i]-b[i]*k[i]) dp[x]=max(dp[x],dp[x]+a[i]b[i]k[i])即剩余天数剩余x天时,为其设定上限值a[i]-b[i]*k[i]由此将贷款有选择地安排入n个天数中,最终被更新到的即为答案

AC代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
int dp[505];
int a[505],b[505],c[505];
int no[505];
int32_t main()
{
 	int n;
 	scanf("%lld",&n);
 	for(int i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
 	for(int i=1;i<=n;i++) no[i]=i;
 	sort(no+1,no+1+n,[&](const int &i,const int &j){return b[i]>b[j];});
 	int ans=0;
 	memset(dp,0,sizeof(dp));
 	for(int k=1;k<=n;k++)
 	{
 		for(int j=n-1;j>=0;j--)
 		{
 			int i=no[k];
 			dp[j+1]=max(dp[j+1],dp[j]+a[i]-b[i]*j);
 			dp[j]=max(dp[j],dp[j]+a[i]-b[i]*c[i]);
 			ans=max({ans,dp[j],dp[j+1]});
 		}
 	}
 	cout<<ans<<endl;
 }

G. Vasya and Maximum Profit

题目大意

给出一个长度为n的试题序列,每个试题有一个难度值 d i ​ d_i​ di价格 c i ​ c_i​ ci,每选择一个试题所得到的的收益为

  1. 付出 c i c_i ci的试题售价
  2. 得到a的试题奖励
  3. g a p ( l , r ) = m a x l ≤ i ≤ r ( d i + 1 − d i ) 2 gap(l,r)={max}_{l\le i\le r}(d_{i+1}-d_i)^2 gap(l,r)=maxlir(di+1di)2假如选择了一段连续的试题序列(l,r)则需要付出gap(l,r)的代价,如l=r则gap(l,r)=0

问选择一段试题序列以求获得最大收益

解题思路

对段(l,r)的收益可以写成 ∑ i = 1 r ( a − c i ) − ∑ i = 1 l ( a − c i ) − g a p ( l , r ) \sum_{i=1}^{r} (a-c_i)-\sum_{i=1}^{l}(a-c_i)-gap(l,r) i=1r(aci)i=1l(aci)gap(l,r)

因此只需要对每个i维护出 l &lt; r , m i n ( ∑ i = 1 l ( a − c i ) + g a p ( l , r ) ) ​ l&lt;r,min(\sum_{i=1}^{l}(a-c_i)+gap(l,r))​ l<r,min(i=1l(aci)+gap(l,r))即可

其中 ∑ i = 1 l ( a − c i ) \sum _{i=1}^{l}(a-c_i) i=1l(aci)可以较简单地直接通过前缀和维护,而 g a p ( l , r ) gap(l,r) gap(l,r)则可以通过对选择每一个i向前直到第一个j ( d j + 1 − d j ) 2 (d_{j+1}-d_j)^2 (dj+1dj)2比第i项大的,其间(j+1,i)的gap(j+1,i)就都为 ( d i + 1 − d i ) 2 (d_{i+1}-d_i)^2 (di+1di)2再通过简单的相加就可以得到(j+1,r)之间最小的 ∑ i = 1 l ( a − c i ) + g a p ( l , r ) \sum_{i=1}^{l}(a-c_i)+gap(l,r) i=1l(aci)+gap(l,r)而随着右端点的移动可以不断更新最新的min而将原来的删去,为此我们需要用到multiset和stack维护.详情见代码

AC代码

#include<stack>
#include<set>
#include<cstdio>
#include<iostream>
#define int long long 
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
const int size=3e5+5;
stack<pii> Sta;
multiset<LL> ms;
LL c[size];int d[size];
inline LL calc(int i){return (d[i+1]-d[i])*(d[i+1]-d[i]);}
int32_t main()
{
	int n,a;
	LL ans=0;
	scanf("%lld%lld",&n,&a);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&d[i],&c[i]);
		c[i]= c[i-1]+a-c[i];ans=max(ans,c[i]-c[i-1]);
	}
	for(int i=1;i<n;i++)
	{
		if(!ms.empty()) ans=max(ans,c[i]-*ms.begin());
		LL loop=calc(i),bs=c[i-1];
		while(!Sta.empty()&&calc(Sta.top().first)<=loop)
			bs=min(bs,Sta.top().second),ms.erase(ms.find(Sta.top().second+calc(Sta.top().first))),Sta.pop();
		Sta.push(pii(i,bs)),ms.insert(loop+bs);
	}
	if(!ms.empty()) ans=max(ans,c[n]-*ms.begin());
	printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值