Codeforces Round #618(Div. 2) (全题解)

Codeforces Round #618(Div. 2) (全题解)

A. Non-zero

洛谷补题传送门

思路:要满足乘积不为0,只需要所有的数均不为0即可;要满足和不为0,在任意一个数加一即可。因此只需要记录0的数量cnt和所有数的和sum,让所有的0加一之后,看和是否为0,如果和为0,再找一个不是-1的数加一即可。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define maxn	200005

int t, sum, cnt, n;// cnt 记录0的个数,sum记录所有数的和

int main()
{
	cin >> t;
	while (t--)
	{
		cin >> n;
		cnt = 0; sum = 0;//初始化
		while (n--)
		{
			int tmp;
			cin >> tmp;
			if (tmp == 0)	cnt++;
			sum += tmp;
		}
		cout << cnt + (sum + cnt == 0) << endl; // 对所有0加一之后如果和为0还要再进行一次操作
	}
	return 0;
}

B. Assigning to Classes

洛谷补题传送门

思路:可以验证,将数列排序后对半分,两个中位数必不可能落在同一边。所以,取最中间的两个数作为两个中位数是最优的选择。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define maxn	200005

int n, t;
int a[maxn];

int main()
{
	cin >> t;
	while (t--)
	{
		cin >> n;
		for (int i = 0; i < (n << 1); i++)
		{
			cin >> a[i];
		}
		sort(a, a + (n << 1));
		cout << a[n] - a[n - 1] << endl;
	}
	return 0;
}

C. Anu Has a Function

洛谷补题传送门

思路:先分析 f ( x , y ) = ( x ∣ y ) − y f(x,y)=(x|y)-y f(x,y)=(xy)y,对于|运算,其与+运算不同的地方在于 1 ∣ 1 = 1 1|1=1 11=1,而 1 + 1 = ( 10 ) 2 1+1=(10)_2 1+1=(10)2,所以 f ( x , y ) = ( x + y ) − y − ( ( x + y ) − ( x ∣ y ) ) = x − ( 二 进 制 下 x 与 y 同 是 1 的 部 分 组 成 的 数 ) f(x,y)=(x+y)-y-((x+y)-(x|y))=x-(二进制下x与y同是1的部分组成的数) f(x,y)=(x+y)y((x+y)(xy))=x(xy1),例如 f ( 27 , 14 ) = 27 − ( ( 11011 ) 2 和 ( 1110 ) 2 同 是 1 的 部 分 组 成 的 数 ) = 27 − ( 1010 ) 2 = 17 f(27,14)=27-((11011)_2和(1110)_2同是1的部分组成的数)=27-(1010)_2=17 f(27,14)=27((11011)2(1110)21)=27(1010)2=17。因此原式就相当于 a 1 − ( 二 进 制 下 a 1 与 a 2 ∣ a 3 ∣ . . . ∣ a n 同 是 1 的 部 分 组 成 的 数 ) a_1-(二进制下a_1与a_2|a_3|...|a_n同是1的部分组成的数) a1(a1a2a3...an1),因此我们只要把 a 1 a_1 a1选出来就行了。

对所有的位上面的1的数量进行统计,如果多于一个数或者没有一个数在某一位上面是1的话,那么无论 a 1 a_1 a1是哪个数,结果的这一位上一定是0;如果只有一个数在某一位上面是1的话,只要让这个数作为 a 1 a_1 a1,那么最终结果这一位上就是1了。因此,要取满足这样的条件的 t t t n n n :只有一个数在第 t t t 位是1,而这个数的下标是 n n n。要使结果最大,则 t t t 要最大,此时将原数列的 a n a_n an作为新数列的 a 1 a_1 a1即可。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define maxn	200005

int n, t, id = -1;// 记录要放在第一的数的下标
int b[maxn];
int a[100][2];// a[i][0]记录有多少个数在第i位是1,a[i][1]记录最近的在第i位是1的数的下标

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int tmp, t = 0;//用tmp来遍历所有的位,t表示当前遍历到的位数
		cin >> tmp;
		b[i] = tmp;
		while (tmp)
		{
			if (tmp & 1)//如果该位是1
			{
				a[t][0]++;
				a[t][1] = i;
			}
			tmp >>= 1;
			t++;
		}
	}

	for (int i = 40; i >= 0; i--)//从高到低遍历,遇到第一个满足条件的位数t即停止
	{
		if (a[i][0] == 1)
		{
			id = a[i][1];
			break;
		}
	}

	if (id == -1)//如果没有满足条件的位数t,则说明随便排结果都一样是0
	{
		for (int i = 0; i < n; i++)
		{
			cout << b[i] << ' ';
		}
		return 0;
	}
	cout << b[id];//将第id个数先打印放在第一
	for (int i = 0; i < n; i++)
	{
		if (i != id)//跳过第id个数依次打印
			cout << ' ' << b[i];
	}
	return 0;
}

D. Aerodynamic

洛谷补题传送门

思路:先随便尝试了一下,发现平行四边形,拉长的正六边形什么的都符合条件,而三角形,五边形什么的都不行,于是猜测只要对边平行且相等的2k边形均符合条件。写完就过了。没有严谨证明。(不知道是不是这样)

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define maxn	100005
#define ll		long long

int n;
ll a[maxn][2];

int judge_para(ll x1, ll y1, ll x2, ll y2) // 判断两个向量是否刚好互为相反向量
{
	return x1 == -x2 && y1 == -y2;
}

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		scanf("%lld%lld", &a[i][0], &a[i][1]);
	}
	if (n & 1)	printf("NO");// n为奇数连对边都找不到
	else // 只要满足对边平行且相等(对边对应的向量相等或者相反),即能输出YES
	{
		if (!judge_para(a[0][0] - a[n - 1][0], a[0][1] - a[n - 1][1],
			a[n >> 1][0] - a[(n >> 1) - 1][0], a[n >> 1][1] - a[(n >> 1) - 1][1]))
		{
			printf("NO");
		}
		else
		{
			for (int i = 1; i < (n >> 1); i++)
			{
				if (!judge_para(a[i][0] - a[i - 1][0], a[i][1] - a[i - 1][1],
					a[(n >> 1) + i][0] - a[(n >> 1) + i - 1][0], a[(n >> 1) + i][1] - a[(n >> 1) + i - 1][1]))
				{
					printf("NO");
					return 0;
				}
			}
			printf("YES");
		}
	}
	return 0;
}

E. Water Balance

洛谷补题传送门

思路:最优的结果一定是一个非递减的序列,否则可以将连续递减的子序列进行“平均”处理即可得到更优的解。因此很简单的想法就是维护序列的非递减性。

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define maxn	1000005
#define ll		long long

int n, top = -1;// top是当前最大的下标,类比栈
ll a[maxn][2];// a[i][0]代表第i个块的总和,a[i][1]代表第i个块包含的数的个数
// 记录和(整数)比记录平均值(浮点数)更准确

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		top++;
		scanf("%lld", &a[top][0]);//先写入数组的结尾
		a[top][1] = 1;// 新的块包含1个数
        // 检查是否需要合并,如果新合并的块比前一个块的平均值小则进行合并
		while (top > 0 && a[top][0] * a[top - 1][1] < a[top - 1][0] * a[top][1])// 用乘法代替除法进行比较,效率和准确性都更高
		{
			top--;
			a[top][0] += a[top + 1][0];
			a[top][1] += a[top + 1][1];
		}
	}
    // 每一块内部都进行平均处理
	for (int i = 0; i <= top; i++)
	{
		double tmp = (double)a[i][0] / (double)a[i][1];
		for (int j = 0; j < a[i][1]; j++)
		{
			printf("%.10lf\n", tmp); // 输出10位小数,保证误差不超过1e-10
		}
	}
	return 0;
}

总结

这次感觉不算太难,但是被第四题卡了太久,还是要继续努力啊!

你可能感兴趣的:(Codeforces Round #618(Div. 2) (全题解))