Codeforces Round #608 (Div. 2)

今天状态还不错,补题补得比较顺利,div2 Solved:5 out of 6
A.
将b,c作为一个整体,个数为b,c的较小值;然后根据e,f的大小,决定先买a,还是先买b,c这个整体。如果先买了,就要贪心的尽可能买光。
#include <bits/stdc++.h>
using namespace std;
int a,b,c,d,e,f,ans1,ans2,sum;
int main(){
	scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f);
	b=min(b,c);
	if (e<f)
	{
		ans2=min(b,d);
		d-=ans2;
		sum+=ans2*f;
		ans1=min(a,d);
		sum+=ans1*e;
	}
	else
	{
		ans1=min(a,d);
		d-=ans1;
		sum+=ans1*e;
		ans2=min(b,d);
		sum+=ans2*f;
	}
	printf("%d\n",sum);
return 0;	
}
B.
构造一种方案使得整一行的颜色均相同,要求改变次数<=3n。
用一种改变次数<=2n的做法很方便:先假设最终整行都变成了第一个格子的颜色,那么对于【2,n-1】的格子来说,如果当前颜色和第一个格子颜色不同,就改变这个格子和后面的格子颜色,这样操作后可以保证【1,n-1】的格子颜色均相同了。
现在有3种情况:
1.第n个格子颜色恰好相同,结束操作;
2.n-1是一个偶数,那么就把【1,n-1】的格子每隔一个染色,染成和第n个格子一样的颜色;
3.n-1是一个奇数,无法完成染色。
#include <bits/stdc++.h>
using namespace std;
const int N=2e2+5;
int n,tot;
int a[N],ans[N];
char str[N];
int main(){
	scanf("%d",&n);
	scanf("%s",str+1);
	for (register int i=1; i<=n; ++i) if (str[i]=='B') a[i]=1; 
	for (register int i=2; i<n; ++i)
	if (a[i]!=a[1])
	{
		a[i]^=1; a[i+1]^=1;
		ans[++tot]=i;
	}
	if (a[n]!=a[1] && (n-1)%2==1) printf("%d\n",-1);
	else 
	{
		if (a[n]!=a[1])
		{
			for (register int i=1; i<=n-1; i+=2) ans[++tot]=i;
		}
		printf("%d\n",tot);
		for (register int i=1; i<=tot; ++i) printf("%d ",ans[i]);
	}
return 0;
}
C.
将平面根据学校的位置分成8个部分:
分别为:在学习左上,左下,右上,右下,正左,正右方,正上,正下。
可以发现,如果帐篷放在(x,y+1),那么左上,右上,正上的学生可以买到帐篷;同理可得放在(x,y-1),(x+1,y),(x-1,y)。
把这四个值取一个最大值即可。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,xx,yy,ans1,ans2,ans3,ans4,sum1,sum2,sum3,sum4,ans,ansx,ansy;
int x[N],y[N];
int main(){
	scanf("%d%d%d",&n,&xx,&yy);
	for (register int i=1; i<=n; ++i) scanf("%d%d",&x[i],&y[i]);
	for (register int i=1; i<=n; ++i)
	{
		if (x[i]<xx && y[i]<yy) ans1++;
		if (x[i]<xx && y[i]>yy) ans2++;
		if (x[i]>xx && y[i]<yy) ans3++;
		if (x[i]>xx && y[i]>yy) ans4++;
		if (x[i]==xx && y[i]<yy) sum1++;
		if (x[i]==xx && y[i]>yy) sum2++;
		if (x[i]<xx && y[i]==yy) sum3++;
		if (x[i]>xx && y[i]==yy) sum4++;
	}
	ans=max(ans,ans1+ans2+sum3);
	ans=max(ans,ans1+ans3+sum1);
	ans=max(ans,ans3+ans4+sum4);
	ans=max(ans,ans2+ans4+sum2);
	if (ans==ans1+ans2+sum3) ansx=xx-1,ansy=yy;
	if (ans==ans1+ans3+sum1) ansx=xx,ansy=yy-1;
	if (ans==ans3+ans4+sum4) ansx=xx+1,ansy=yy;
	if (ans==ans2+ans4+sum2) ansx=xx,ansy=yy+1;
	printf("%d\n",ans);
	printf("%d %d\n",ansx,ansy);
return 0;	
}
D.
一种方法是贪心地做背包dp,既然要贪心了,为何还要贪心地做dp呢?一贪到底不好吗?
所以我写了可反悔贪心。
此题可以贪心,是基于每个城市只需要一个士兵来驻守,如果需要ith个士兵来驻守,就不能贪心了。
由于无论是早驻守还是晚驻守,收益均相同,所以当然是晚驻守会使得更容易通关,我们统计出每个城市能够被驻守的最晚时间,然后决定:不到最后一刻,坚决不驻守士兵。
弄清楚了这个原则后,就是一道基础的可反悔贪心了,用一个小根堆维护即可。
#include <bits/stdc++.h>
using namespace std;
const int N=5e3+5,M=3e5+5;
int n,m,k,u,v,ans;
int a[N],b[N],c[N],maxn[N];
int cnt,head[N];
struct edge{int next,to;}e[M]; 

inline void add(int u,int v)
{
	cnt++;
	e[cnt].next=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}

priority_queue< int,vector<int>,greater<int> >q;

int main(){
	scanf("%d%d%d",&n,&m,&k);
	for (register int i=1; i<=n; ++i) scanf("%d%d%d",&a[i],&b[i],&c[i]); 
	for (register int i=1; i<=n; ++i) maxn[i]=i;
	for (register int i=1; i<=m; ++i)
	{
		scanf("%d%d",&u,&v),add(u,v);
		maxn[v]=max(maxn[v],u);	
	}
	
	for (register int i=1; i<=n; ++i)
	{
		while (k<a[i]) 
		{
			if (q.size())
			{
				int u=q.top(); q.pop();
				ans-=u;
				k++;
			}
			else
			{
				puts("-1");
				return 0;
			}
		}
		k+=b[i];
		if (maxn[i]==i)
		{
			if (k) k--,ans+=c[i],q.push(c[i]);
			else if (q.size())
			{
				if (c[i]>q.top()) 
				{
					ans-=q.top(),q.pop();
					ans+=c[i],q.push(c[i]);	
				}
			}
		}
		for (register int j=head[i]; j; j=e[j].next)
		if (maxn[e[j].to]==i)
		{
			if (k) k--,ans+=c[e[j].to],q.push(c[e[j].to]);
			else if (q.size())
			{
				if(c[e[j].to]>q.top())
				{
					ans-=q.top(),q.pop();
					ans+=c[e[j].to],q.push(c[e[j].to]); 
				}
			}
		}
	}
	printf("%d\n",ans);
return 0;
}
E.
这个函数变来变去,好像在数学中总是出现。
一眼看去,答案x具有单调性,所以二分枚举,现在就需要我们迅速统计出当答案为x时再【1,n】的范围内有多少符合条件的数字。
找了一波规律以后才发现,对于奇数和偶数的统计是不同的,原来,奇数有奇数的单调性,偶数有偶数的单调性。
修正刚刚的结论,把奇偶分开做两次二分答案取max即可。
统计符合条件的数字,我用了log(n)的复杂度,如果想更加优化的话,还可以套个二分,这个理论复杂度会变成log(log(n)),但是实际常数应该挺大的。
本人代码复杂度:log(n)*log(n);理论最优复杂度:log(n)*log(log(n))。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,k,l,r,mid,ans1,ans2;
signed main(){
	scanf("%lld%lld",&n,&k);
	l=1ll; r=n/2ll;
	while (l<=r)
	{
		mid=l+r>>1ll;
		int now=mid*2ll;
		int i=0ll;
		int sum=0ll;
		while ((now<<i)+(1ll<<(i+1ll))-1ll<=n) sum+=(1ll<<(i+1ll)),i++;
		if ((now<<i)<=n) sum+=n-(now<<i)+1ll;
		if (sum>=k) ans1=now,l=mid+1ll;
		else r=mid-1ll;
	}
	l=1ll; if (n%2ll==1ll) r=n/2ll+1; else r=n/2ll;
	while (l<=r)
	{
		mid=l+r>>1ll;
		int now=mid*2ll-1ll;
		int i=0ll;
		int sum=0ll;
		while ((now<<i)+(1ll<<i)-1ll<=n) sum+=(1ll<<i),i++;
		if ((now<<i)<=n) sum+=n-(now<<i)+1ll;
		if (sum>=k) ans2=now,l=mid+1ll;
		else r=mid-1ll;
	}
	printf("%lld\n",max(ans1,ans2));
return 0;
}
F.
待小蒟蒻去看看题解…
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值