Codeforces Global Round 2 题解

版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/89222173

题目链接:http://codeforces.com/contest/1119/problem/A

A. Ilya and a Colorful Walk

两点的最远距离是相互的,所以其中一个点肯定是左右端点。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 3e5 + 10;
int a[mx];
int main()
{   
	int n,ans = 0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",a+i);;
	int l = n;
	while(a[l]==a[1]) l--;
	ans = max(ans,l-1);l = 1;
	while(a[l]==a[n]) l++;
	ans = max(ans,n-l); 
	printf("%d\n",ans);
    return 0;
}

B. Alyona and a Narrow Fridge

答案肯定满足连续单调性,所以我们去二分答案,这时就确定了那些瓶子肯定要放进去。这个时候我们发现问题就很简单了,排序所有瓶子的高度,然后两个两个放肯定是最优的。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e3 + 10;
int a[mx],b[mx];
int main()
{   
	int n,m,v;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",a+i);
	}
	int l = 1,r = n;
	while(l<r){
		int mid = (l+r+1)>>1;
		ll hi = 0;
		for(int i=1;i<=mid;i++) b[i] = a[i];
		sort(b+1,b+1+mid);
		if(mid&1){
			hi = b[1];
			for(int i=3;i<=mid;i+=2) hi += b[i];
		}else for(int i=2;i<=mid;i+=2) hi += b[i];
		if(hi<=m) l = mid;
		else r = mid-1;
	}
	printf("%d\n",l);
    return 0;
}

C. Ramesses and Corner Inversion

考虑缩小问题和矩阵的等价性,我们可以把A矩阵行[1,n)和列[1,m)上的点都变成和B矩阵相同,最后就剩下最后一列和最后一行不确定。此时变换的矩阵和原来的A是等价的。而且我们把问题缩小到只考虑最后一行和最后一列。最后我们发现想要改变最后一行或者最后一列必须改变已经相同的点,所以最后一行和最后一列必须和B相同才行。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 5e2 + 10;
int a[mx][mx],b[mx][mx];
int main()
{   
	int n,m,v;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)
		scanf("%d",a[i]+j);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",b[i]+j);
		}
	}
	for(int i=1;i<n;i++){
		for(int j=1;j<m;j++){
			if(a[i][j]!=b[i][j]){
				a[i][j] ^= 1;
				a[i][j+1] ^= 1;
				a[i+1][j] ^= 1;
				a[i+1][j+1] ^= 1;
			}
		}
	}
	for(int i=1;i<=n;i++) if(a[i][m]!=b[i][m])
	return 0*puts("No");
	for(int j=1;j<=m;j++) if(a[n][j]!=b[n][j])
	return 0*puts("No");
    return 0*puts("Yes");
}

D. Frets On Fire

很明显对于区间大小相同的区间来说他们的不同的数的个数是一样的。对此我们就可以把左端点L固定到0了。

然后在考虑最s进行排序,假设查询的区间大小为W,对于已经排序的s来说,最大的结果就是sn+W。

那么它的前提是所有的n段没有空隙,如果有空隙那么就减掉这些就是答案了。对于已经排序的s来说要求“空隙”应该是很容易。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
ll a[mx],b[mx],l,r;
ll suf[mx];
int main()
{   
	int n,q;
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%lld",a+i);
	sort(a,a+n);
	n = unique(a,a+n) - a;
	for(int i=1;i<n;i++)
	b[i-1] = a[i] - a[i-1];
	n--,sort(b,b+n);
	for(int i=n-1;i>=0;i--)
	suf[i] = suf[i+1] + b[i];
	scanf("%d",&q);
	while(q--){
		scanf("%lld%lld",&l,&r);
		l = r - l + 1;
		int k = upper_bound(b,b+n,l) - b;
		ll ret = suf[k] - (n-k)*l;
		printf("%I64d ",a[n]+l-ret-a[0]);
	}
    return 0*puts(""); 
}

E. Pavel and Triangles

很明显所有的三角形都只能是等腰三角形。也就是必须二对一的配对。

并且我们发现它满足小的优先规则。也就是当我们考虑2^k次方的时候可以让1-(k-1)的木条先做,然后再考虑2^k的木条怎么放。

那么就可以用dp来做了,假设1到k-1的木条还有m根没有配对,那么就有如下两种情况:

1.m>=a[k]/2.那么这个时候就是拿两个两个2^k的木条去配对是最优的。

2.m<a[k]/2,,那么此时前1到k-1次方的所有木条都可以加入组合,所以答案可以是S[k]/3。

另一种做法可以直接贪心去做,不过要先考虑只有一根木条长度的先组合。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 3e5 + 10;
int main()
{   
	int n,a;
	ll ans = 0,s = 0;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&a);
		s += a;
		ans = min(s/3,ans+a/2);
	}
	printf("%lld\n",ans);
    return 0; 
}
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 3e5 + 10;
int a[mx],q[mx],hd;
int main()
{   
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",a+i);
		if(a[i]==1) q[++hd] = i;
	}
	int l = 0;ll ans = 0;
	for(int i=n-1;i>=0;i--){
		if(a[i]>1){
			if(a[i]&1) ans++,a[i] -= 3;
			int ret = a[i] / 2;
			while(ret&&hd){
				if(q[hd]<i) ret--,ans++;
				a[q[hd]] = 0,hd--;
			}
			while(l<i){
				if(ret<a[l]){
					a[l] -= ret;
					ans += ret,ret = 0;
					break;
				}else{
					ans += a[l];
					ret -= a[l],a[l] = 0;
					l++;
				}
			}
			if(ret){
				ans += ret*2 / 3;
				break;
			}
		}
	} 
	printf("%lld\n",ans);
    return 0; 
} 

G. Get Ready for the Battle

如果去考虑按顺序打下去,首先打第一队敌人,一直让所有队伍都去打第一队,直到第一队敌人剩下k1<n.这时我们可以让一些队伍的人数组合刚好等于k1,保证了第一队可以恰好打完,这时又将所有的队伍都去打第二队敌人,直到第二队敌人剩下k2<n,接下来我们又想办法使得一些队伍人数组合刚好等于k2.这样一直下去到km.

那么怎么让一些队伍的组合都k1,k2,k3...km呢,我们可以排序k,然后值得ki = ki - ki-1。这样就可以得到我们的m支队伍了,但要保证队伍人数总和等于n,所以我们让km = n,这样就使得了至于处理最后一支敌人队伍的时候才会浪费我们队伍的人数。所以攻击次数一定是(Sum - 1) / n + 1。

#include <bits/stdc++.h>
using namespace std;
const int mx = 1e6 + 10;
int a[mx],b[mx];
int main(){
	int n,m,sum = 0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d",a+i);
		b[i] = (b[i-1]+a[i])%n;
		sum += a[i]; 
	}
	b[m] = n;
	sort(b+1,b+1+m);
	printf("%d\n",(sum-1)/n+1);
	for(int i=m;i;i--) b[i] = b[i] - b[i-1];
	for(int i=1;i<=m;i++) printf("%d ",b[i]);
	puts("");int j = 1;
	for(int i=1;i<=n;i++){
		while(a[i]>0){
			a[i] -= b[j];
			printf("%d ",i);
			j++;
			if(j==m+1) j = 1,puts("");
		}
	}
	while(j>1&&j<=m) printf("1 "),j++;	
	return 0*puts("");
} 

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/89222173