Round #737 (Div. 2)

A. Ezzat and Two Subsequences

原题链接

题目大意

给出一个长度为 n n n的序列,将这个序列分成非空的两个子序列,使得这两个子序列的平均值之和最大,求最大的平均值之和。组数 t t t 1 ≤ t ≤ 1 0 3 1≤t≤10^3 1t103 2 ≤ n ≤ 105 2≤n≤105 2n105 − 1 0 9 ≤ a i ≤ 1 0 9 −10^9≤a_i≤10^9 109ai109,题目保证 n n n的和不超过 3 ⋅ 1 0 5 3⋅10^5 3105

思路

s o r t sort sort一下,保证数组单增,然后计算前缀和。枚举 i i i,表示子序列 x x x 1... i 1...i 1...i,子序列 y y y i + 1... n i+1...n i+1...n,利用前缀和 O ( 1 ) O(1) O(1)地计算子序列 x x x y y y的平均值之和,更新答案。
没论证为什么这个是对的,赛时没 s o r t sort sort W A WA WA了两发, s o r t sort sort之后就过了。貌似是贪心之类的。

代码
#include <bits/stdc++.h>
using namespace std;
long long n, a[100010], t, sum[100010];
double ans;
int main()
{
	scanf("%lld", &t);
	while(t--)
	{
		ans = -19260817192;
		scanf("%lld", &n);
		for(int i = 1; i <= n; i++)
			scanf("%lld", &a[i]);
		sort(a+1, a+n+1);
		for(int i = 1; i <= n; i++)
			sum[i] = sum[i-1] + a[i];
		for(int i = 1; i < n; i++)
		{
			double l, r;
			l = (double)sum[i]/i;
			r = (double)(sum[n]-sum[i])/(n-i);
			if(l+r > ans)
				ans = l+r;
		}
		printf("%.12lf\n", ans);
	}
	return 0;
}

B. Moamen and k-subarrays

原题链接

题目大意

有一个长度为 n n n的序列,序列里的元素各不相同。现在要通过如下操作将这个序列排成非降序: 1. 1. 1.将这个序列分成 k k k个非空子串,每一个元素都在一个子串里。 2. 2. 2.将这 k k k个子串的顺序任意地重新排列。 3. 3. 3.将排序后的 k k k个子串合并。问能否通过这样的操作使得序列排成非降序。组数 t t t 1 ≤ t ≤ 1 0 3 1≤t≤10^3 1t103 1 ≤ k ≤ n ≤ 1 0 5 1≤k≤n≤10^5 1kn105 0 ≤ ∣ a i ∣ ≤ 1 0 9 0≤|ai|≤10^9 0ai109,题目保证 n n n的和不超过 3 ⋅ 1 0 5 3⋅10^5 3105

思路

如果可以通过这样的操作使得原序列排成非降序,那么每次选取的子串一定是连续递增的,也就是别的子串里不应该有后一个元素和当前元素之间的元素,否则合并后一定不合法。所以先将所有的元素离散化,然后统计所有的连续递增子串的个数,如果小等 k k k则输出 Y E S YES YES,否则输出 N O NO NO。统计的时候连续递增子串就是后一个元素比前一个元素大 1 1 1 的子串。

代码
#include <bits/stdc++.h>
using namespace std;
int n, t, k, a[100010], b[100010], cnt, now;
int main()
{
	scanf("%d", &t);
	while(t--)
	{
		cnt = 0;
		now = -1;
		scanf("%d %d", &n, &k);
		for(int i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
			b[i] = a[i];
		}
		sort(b+1, b+n+1);
		for(int i = 1; i <= n; i++)
			a[i] = lower_bound(b+1, b+n+1, a[i]) - b;
		for(int i = 1; i <= n; i++)
		{
			if(a[i] - now != 1)
				cnt++;
			now = a[i];
		}
		if(cnt <= k)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

C. Moamen and XOR

原题链接

题目大意

一个长度为 n n n的序列,序列里的元素非负且小于 2 k 2^k 2k,求使得 a 1 a_1 a1 & \& & a 2 a_2 a2 & \& & a 3 a_3 a3 & … & \&…\& && a n ≥ a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n a_n≥a_1\oplus a_2\oplus a_3\oplus…\oplus a_n ana1a2a3an成立的序列的个数。答案对 1 e 9 + 7 1e9+7 1e9+7取模。组数 t t t 1 ≤ t ≤ 5 1≤t≤5 1t5 1 ≤ n ≤ 2 ⋅ 1 0 5 1≤n≤2⋅10^5 1n2105 0 ≤ k ≤ 2 ⋅ 1 0 5 0≤k≤2⋅10^5 0k2105 & \& &:位运算与, ⊕ \oplus :位运算异或。

思路

由题意(数据范围)知,一定是一位一位拆开来做的。
a i a_i ai拆成 k k k位,每一位都是 0 0 0 1 1 1,那么合法的序列就是对于每一位 01 01 01,有 n n n & \& & 操作后的结果 ≥ \geq n n n ⊕ \oplus 操作后的结果。
对于每一位,显然 & \& & 操作的结果只有全为 1 1 1时才是 1 1 1,而此时 ⊕ \oplus 操作的结果与 1 1 1的个数有关,所以考虑奇偶。
如果 n n n是奇数,那么 n n n个数的这一位全为 1 1 1 & \& & 操作结果是 1 1 1 ⊕ \oplus 操作结果也是 1 1 1。对于其余 & \& & 操作结果是 0 0 0的情况,如果想要满足条件,就只有 ⊕ \oplus 操作结果也是 0 0 0,而这种情况必须 1 1 1的个数是偶数,那么枚举奇数个 0 0 0的情况,利用组合数(高中排列组合题,全排列消序)算出每种情况的方案数,求和。最终答案就是一位时方案数的 k k k次幂。
如果 n n n是偶数,那么 n n n个数的这一位全为 1 1 1 & \& & 操作结果是 1 1 1,而 ⊕ \oplus 操作结果是 0 0 0。满足合法条件的情况是第 i i i位前结果相等,第 i i i & \& & 操作结果为 1 1 1 ⊕ \oplus 操作结果为 0 0 0,之后结果可以任意。 1 1 1的个数是偶数时 ⊕ \oplus 操作结果是 0 0 0,所以枚举偶数个 0 0 0的情况,利用组合数算出每种情况的方案数,求和。最终答案是方案数的 i − 1 i-1 i1次幂乘上 2 n 2^n 2n k − i k-i ki次幂(要么 0 0 0要么 1 1 1的总排列数)再加上两种操作结果每一位都相同时的答案。
阶乘和阶乘的逆元要预处理出来。

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10, mod = 1e9+7;
int t, n, k, ans, sum;
int pro[N], inv[N];
int fpm(ll x, int power) 
{
    x %= mod;
    int a = 1;
    for (; power; power >>= 1, x = x*x%mod)
	    if(power & 1) a = a*x%mod;
    return a;
}
void init()
{
	pro[0] = 1;
	for(int i = 1; i < N; i++)
		pro[i] = (ll)pro[i-1]*i%mod;
	inv[N-1] = fpm(pro[N-1], mod-2);
	for(int i = N-1; i; i--)
		inv[i-1] = (ll)inv[i]*i%mod;
}
int C(int n, int m)
{
	return (ll)pro[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	init();
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d %d", &n, &k);
		sum = ans = 0;
		if(n % 2 == 1)
		{
			sum += 1;
			for(int i = 1; i <= n; i += 2)
				sum = (ll)(sum + (ll)C(n, i))%mod;
			ans = fpm(sum, k); 
		}
		else
		{
			for(int i = 2; i <= n; i += 2)
				sum = (ll)(sum + (ll)C(n, i))%mod;
			int er = fpm(2, n);
			for(int i = 1; i <= k; i++)
				ans =(ll)(ans + (ll)fpm(sum, i-1)*fpm(er, k-i)%mod)%mod;
			ans = (ll)(ans + (ll)fpm(sum, k))%mod;
		}
		printf("%d\n", ans);
	}
	return 0;
}

这场比赛是自打牛客罚坐以来打的最爽的一次了,多少让自己觉得其实也没那么废物。 R a n k Rank Rank 2332 2332 2332,继续努力吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值