Codeforces Global Round 2 部分题解

28 篇文章 0 订阅
24 篇文章 0 订阅

BG


周六的时候不太敢打,现在来看前几题还是很温暖的。。

A


n个数字,找到两个位置i和j使得a[i]!=a[j]且i-j最大

sb题,随便做。我写了动态开点线段树来维护前缀后缀min

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int INF=0x3f3f3f3f;
const int N=300005;

std:: map <int,int> last;

int min[N<<2];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void modify(int now,int tl,int tr,int x,int v) {
	min[now]=std:: min(min[now],v);
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(now<<1,tl,mid,x,v);
	else modify(now<<1|1,mid+1,tr,x,v);
}

int query(int now,int tl,int tr,int l,int r) {
	if (r<l) return INF;
	if (tl>=l&&tr<=r) return min[now];
	int mid=(tl+tr)>>1,qx=INF,qy=INF;
	if (l<=mid) qx=query(now<<1,tl,mid,l,r);
	if (mid+1<=r) qy=query(now<<1|1,mid+1,tr,l,r);
	return std:: min(qx,qy);
}

void build(int now,int tl,int tr) {
	min[now]=INF;
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	build(now<<1,tl,mid),build(now<<1|1,mid+1,tr);
}

int main(void) {
	// freopen("data.in","r",stdin);
	int n=read(),ans=0;
	build(1,1,n);
	rep(i,1,n) {
		int x=read();
		int ra=query(1,1,n,1,x-1),rb=query(1,1,n,x+1,n);
		int res=std:: min(ra,rb);
		ans=std:: max(ans,i-res);
		modify(1,1,n,x,i);
	}
	printf("%d\n", ans);
	return 0;
}

B


n个数字,要求把前k个分成若干组,每组最多1个至少2个,每组的权值为这组元素的最大值
最大化k使得每组权值之和不大于给定的h

sb题,我们二分答案,排序然后dp。f[i]表示前i个权值之和最小值

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int N=500005;

LL f[N],a[N],b[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

bool check(LL h,int mid) {
	rep(i,1,mid) b[i]=a[i];
	std:: sort(b+1,b+mid+1);
	f[1]=b[1]; rep(i,2,mid) f[i]=std:: min(f[i-1],f[i-2])+b[i];
	return f[mid]<=h;
}

int main(void) {
	int n=read(); LL h=read();
	rep(i,1,n) a[i]=read();
	int l=1,r=n,ans=1;
	while (l<=r) {
		int mid=(l+r)>>1;
		if (check(h,mid)) l=mid+1,ans=mid;
		else r=mid-1;
	}
	printf("%d\n", ans);
	return 0;
}

C


给两个n*m的01矩阵A和B,每次可以选择A的一个长和宽至少为2的子矩阵,然后翻转子矩阵的四个角落
问能否通过若干轮操作把A变成B

注意到长和宽至少为2,那么每次操作都不会改变一行或一列的异或和
那么如果某一行或某一列的异或和不等就肯定不行了。正确性的话可以感受一下?

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=2005;

int a[N][N],b[N][N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	int n=read(),m=read();
	rep(i,1,n) rep(j,1,m) a[i][j]=read();
	rep(i,1,n) rep(j,1,m) b[i][j]=read();
	rep(i,1,n) {
		int s=0;
		rep(j,1,m) s+=(a[i][j]!=b[i][j]);
		if (s&1) return 0&puts("No");
	}
	rep(j,1,m) {
		int s=0;
		rep(i,1,n) s+=(a[i][j]!=b[i][j]);
		if (s&1) return 0&puts("No");
	}
	puts("Yes");
	return 0;
}

D


有一个n行1e18列的矩阵A,给出第0列的元素,定义A[i,j]=A[i,0]+j
k次询问l,r求矩阵第l列到第r列中不同元素的数量

注意到问题等价于,我们给出n个数轴上的点,k次询问求所有点作为左端点,长度为给出值的区间的并的长度
那么就十分可做了。我们先给s排序,然后求出每个点与下一个点的距离d。那么对于区间相互独立的部分贡献就是数量*(r-l+1),区间交在一起的部分贡献恰好就是d了。这个按照d排序然后二分就行

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <map>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef unsigned long long LL;
const LL INF=5e18;
const int N=500005;

std:: map <LL,bool> map;

LL a[N],d[N],s[N];

LL read() {
	LL x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	// freopen("data.in","r",stdin);
	int w=read(),n=0;
	rep(i,1,w) {
		LL x=read();
		if (!map[x]) {
			map[x]=1;
			a[++n]=x;
		}
	}
	std:: sort(a+1,a+n+1);
	rep(i,1,n) d[i]=a[i+1]-a[i]; d[n]=INF;
	std:: sort(d+1,d+n+1);
	rep(i,1,n) s[i]=s[i-1]+d[i];
	for (int m=read();m--;) {
		LL x=-read(); x+=read();
		int l=1,r=n,pos=0;
		while (l<=r) {
			int mid=(l+r)>>1;
			if (d[mid]<=x) l=mid+1,pos=mid;
			else r=mid-1;
		}
		LL ans=s[pos]+(x+1)*(n-pos);
		std:: cout << ans << " ";
	}
	return 0;
}

E


给n个棒,第i根长度为2i-1,有a[i]根。问最多能组成多少三角形

代码最短题。。。
可以发现组成三角形只能是(a,a,a)或者(a,a,b)这样。一个贪心策略就是我们优先匹配(a,a,b)的,然后剩下的拿去配(a,a,a),多余的留着往后用。

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):v,ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	int n=read();
	LL a=0,ans=0;
	rep(i,1,n) {
		LL x=read();
		LL t=std:: min(a,x/2);
		ans+=t,a-=t,x-=2*t;
		ans+=(x/3),x%=3,a+=x;
	}
	printf("%lld\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值