Codeforces Round #716 (Div. 2)(A - D)

A. Perfectly Imperfect Array

题意
对于每一个测试样例,问是不是存在一个子序列使得他们的乘积不是完全平方数。

思路
只要有一个数不是完全平方数,直接选它当子序列就行了

AC代码

#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 2e3 + 5;
const int N=1005;
using namespace std;

int main() {

	int ncase;
	cin >> ncase;
	while(ncase--){
		ll n;
		cin >> n;
		ll f = 1, num;
		for(int i = 1; i <= n; i++){
			cin >> num;
			if(sqrt(num) * sqrt(num) != num) f = 0;
		}
		if(f) cout << "NO" << endl;
		else cout << "YES" << endl;
	}
	
	return 0;
}

B. AND 0, Sum Big

题意
对于每一个测试样例,在 0 到 2k -1 个数中选择 n 个整数(可以重复选),并满足:

  1. 这 n 个数的 & 运算结果为 0
  2. 这 n 个数的和最大

求出这样的方案数有多少种,结果对 109+7 取模。

思路
n 个数的和最大,肯定要多选 2k -1。
对于这 n 个数,在二进制位下,有 k 位。
最后的 & 运算结果为 0,每一位上一定存在一个数在这个二进制下为 0。
每一个二进制位下有 k 种选择,故而结果为:nk % (109+7)

AC代码

#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 2e3 + 5;
const int N=1005;
using namespace std;

int main() {

	int ncase;
	cin >> ncase;
	while(ncase--){
		ll n, k;
		cin >> n >> k;
		ll res = 1;
		for(int i = 1; i <= k; i++) res = res * n % mod;
		cout << res << endl;
	}
	
	return 0;
}

C. Product 1 Modulo N

题意
在 1 到 n-1 中,选择最长的一个子序列,这个子序列所有数的乘积对 n 取模的结果为 1。

思路


这里我们先明确一个事情:
令 a % b = 1,一定有 gcd(a, b) = gcd(k*b+1, b) = 1,那么 a 和 b 一定互质。


由上可得,我们选择的数 x 必须满足 gcd(x, n) = 1,否则任何数与 x 的乘积 z 都不满足 gcd(z, n) = 1。
所以,我们肯定是选上所有与 n 互质的数。


令这个序列的乘积结果 x = k*n +p。
显然,p = 1 时,直接输出这个子序列即可。
当 p != 1时,输出这个子序列中除 p 以外的所有数即可。

先证明正确性:x / p = k / p * n + 1,嗯,很明显了,gcd(x / n, n) = 1。
再来证明 p 一定会被选上,即 p 与 n 互质。
由 x = k*n +p,得 p = x % n。
gcd(x, n) = gcd(x%n, n) = gcd(p, n) = 1。

完。

AC代码

#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 3e5 + 5;
const int N=1005;
using namespace std;

int main() {

	ll n;
	cin >> n;
	ll cnt = 0, prod = 1;
	for(ll i = 1; i < n; i++){
		if(__gcd(i, n) == 1){
			cnt++;
			prod *= i;
			prod %= n;
		}
	} 
	
	if(prod == 1) cout << cnt << endl;
	else cout << cnt-1 << endl;
	for(ll i = 1; i <= n; i++){
		if(__gcd(i, n) == 1){
			if(prod != 1 && i == prod) continue;
			cout << i << " ";
		}
	}
	cout << endl;
	
	return 0;
}

D. Cut and Stick

题意
给出一个有 n 个元素的数组,有 m 次查询。每次查询,给出一个区间(l, r),问这个区间最少可以划分为多少个子序列,每个子序列中的众数都不超过区间长度(向上取整)的一半。

思路
莫队处理区间众数(模板可以直接copy)。
特别注意向上取整这个点,下边要用这个卡bug。
如果整个区间的众数小于区间长度的一半则不用在额外划分。
如果整个区间的众数大于区间长度的一半:
令区间的长度为 cnt,区间的众数为 x,则区间剩下的数有 cnt - x 个数。
我们把两个众数和一个非众数分到一块,显然,这种方式可以划走更多的众数。
这时一共划分了 cnt - x 个子序列,还剩下cnt - 3*(cnt - x) 个众数。
把剩下的众数都单独划分为一个子序列,这样划分的子序列总数为:cnt - x + cnt - 3 * (cnt - x) = cnt - 2 * (cnt - x) = 2 * x - cnt


感谢林总提供的最优性证明


AC代码

#include <stdio.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=1e9 + 7;
const int maxn = 3e5 + 5;
const int N=1005;
using namespace std;

int a[maxn], ANS = 0, tm[maxn], cnt[maxn], ans[maxn];

typedef struct Node{
	int l, r;
	int id;
} node;

int sqt;
bool cmp(node A, node B){
	if(A.l/sqt == B.l/sqt) return A.l/sqt%2 == 1 ? A.r < B.r : A.r > B.r;
	else return A.l/sqt < B.l/sqt;
}

node q[maxn];

void add(int num){
	--cnt[tm[num]]; 
	ANS = max(ANS, ++tm[num]); 
	++cnt[tm[num]];
}

void subd(int num){
	--cnt[tm[num]]; 
	if(ANS == tm[num] && cnt[tm[num]] == 0) ANS--;
	--tm[num], ++cnt[tm[num]];
}

int main() {

	int n, m;
	scanf("%d %d", &n, &m);
	sqt = sqrt(n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for(int i = 1; i <= m; i++) scanf("%d %d", &q[i].l, &q[i].r);
	for(int i = 1; i <= m; i++) q[i].id = i;
	
	sort(q+1, q+1+m, cmp);
	
	for(int l = 1, r = 0, i = 1; i <= m; i++){
		int ln = q[i].l, rn = q[i].r;
		while(l < ln) subd(a[l++]);
		while(l > ln) add(a[--l]);
		while(r < rn) add(a[++r]);
		while(r > rn) subd(a[r--]);
		ans[q[i].id] = 1;
		if(ANS > (rn-ln+2)/2) ans[q[i].id] = 2 * ANS - (rn-ln+1);
	}
	
	for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值