Codeforces Round #716 (Div. 2) A~D 题解

本场链接:Codeforces Round #716 (Div. 2)

A. Perfectly Imperfect Array

题目大意:给定一个数组,选出若干数使乘积不是平方数。

\(a^2,b^2\)是两个平方数,则有\(a^2*b^2 = (a*b)^2\)显然这个结论还可以推广到更多元素。也就是说任何多个本来就是平方数的元素乘起来必然还是平方数,那么只有当一开始就不存在一个平方数的时候才是有解的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 105;
int a[N];

int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int n;scanf("%d",&n);
		int ok = 0;
		forn(i,1,n)
		{
			scanf("%d",&a[i]);
			int s = sqrt(a[i]);
			if(s * s != a[i])	ok = 1;
		}
		if(ok)	puts("YES");
		else puts("NO");
	}	
    return 0;
}

B. AND 0, Sum Big

题目大意:求满足下列要求的数组的个数:

  • 长度为\(n\)
  • 所有元素在\(0\)\(2^k-1\)之间
  • 所有元素与起来的结果是\(0\)
  • 整个数组的和最大

结果模\(10^9+7\)

考虑满足整个数组和最大的条件:位运算对每一二进制位考虑,由于与起来的结果必然是\(0\),所以只需要在\(n\)个数中选一个数让他的这一位是\(0\)其余的都是\(1\)就可以了,这样可以保证和是最大的。问题的方案数等价于考虑\(k\)位,每一位对应\(n\)个元素,其中有一个是\(0\)。一共有\(k\)步,每一步有\(n\)种选法,答案是\(k^n\)。快速幂即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int MOD = 1e9+7;

ll qpow(ll a,ll b,ll p)
{
	ll res = 1;
	while(b)
	{
		if(b & 1)	res = (res * a) % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}

int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int n,k;scanf("%d%d",&n,&k);
		printf("%lld\n",qpow(n,k,MOD));
	}	
    return 0;
}

C. Product 1 Modulo N

题目大意:给定\(n\),在\([1,n-1]\)中选取任意多个元素,每个元素只能选一次,使他们的乘积模\(n\)\(1\).保证有解,输出具体方案。

先这么考虑:如果我最后得到的乘积\(s\space mod\space n = q \neq 1\),那么当\((s,n)=1\)的时候有\((s/q) \space mod \space n = 1\)。也就是说,假如最后得到的乘积\(s\)是与\(n\)互质的,且\(q\)也是\(s\)的约数,即使不满足条件也可以转回来。

通过这个启发考虑互质的条件,假如一个数\(a,(a,n)\neq 1\),那么选择了\(a\)之后,势必会让乘积的模不能得到\(1\),因为\((a\%n,n) \neq 1\)。这一点可以如此说明:设\((a,n) = z\),\(a \% n = a - q * n\),则\(a \% n = z((a / z) - (n / z) * q)\)则结果必然不为\(1\)。可以得到一个结论:选择的数只会是选择与\(n\)互质的数,否则一定会导致结果不正确。

同时只选择与\(n\)互质的元素也可以回到第一个结论保证最后的结果是正确的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int gcd(int x,int y)
{
	if(y == 0)	return x;
	return gcd(y,x % y);
}

int main()
{
	int n;scanf("%d",&n);
	vector<int> res;int s = 1;
	forn(i,1,n)
	{
		if(gcd(i,n) != 1)	continue;
		s = 1ll*s * i % n;
		res.push_back(i);
	}
	
	int flag = (s != 1 ? s : -1);
	if(flag != -1)	printf("%d\n",(int)res.size() - 1);
	else	printf("%d\n",(int)res.size());
	for(auto& v : res)	if(v != flag)	printf("%d ",v);
    return 0;
}

D. Cut and Stick

题目大意:给定一个数组\(a\)以及\(q\)个询问,每个询问指定一个区间\([l,r]\),求区间最少划分成几个子集,使得子集内的众数出现次数不高于\((m/2)↑\)。其中\(m\)是子集的大小。

首先考虑一个问题:如何维护区间的众数出现的个数。这是一个经典问题,使用离线的莫队算法可以做到\(O(n\sqrt n)\)的复杂度。简单说一下莫队的维护:首先\(cnt\)维护每个数的出现次数,当增加一个元素的时候只需关注更新后的\(cnt\)是否有可能更新成众数维护即可;删除一个元素的时候需要知道当前这个元素是否是这个出现次数的唯一一个元素,所以需要额外维护一个\(c_cnt\)表示出现次数为某个值的出现次数。当删除一个当前是唯一一个出现次数为最大值的元素的时候,需要将众数的出现次数大小减一。

其次考虑众数的出现次数\(f\)对答案的影响,若\(f \leq (m/2)↑\),则答案为\(1\),直接将所有元素放一起就完事了。而当\(f>(m/2)↑\)时,有\(m-f + 1\)个元素可以与众数放在一起组成\(1\)组,剩下\(f - (m - f + 1)\)个元素必须每个元素自成一组,答案就是\(1 + f- (m - f + 1) = 2 * f - m\)。维护\(f\)即可。

注意莫队先处理add操作再处理del操作,否则可能会出现出现次数为负数的元素导致越界。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 3e6+7,len = 550;
int a[N];
struct Query
{
	int id,l,r;
}q[N];
int cnt[N],n,m,ans[N],c_cnt[N],max_c;

inline int get(int x)
{
	return x / len;
}

bool cmp(const Query& a,const Query& b)
{
	int i = get(a.l),j = get(b.l);
	if(i != j)	return i < j;
	return a.r < b.r;
}

inline void add(int x)
{
	--c_cnt[cnt[a[x]]];
	++cnt[a[x]];
	max_c = max(max_c,cnt[a[x]]);
	++c_cnt[cnt[a[x]]];
}

inline void del(int x)
{
	--c_cnt[cnt[a[x]]];
	if(max_c == cnt[a[x]] && !c_cnt[cnt[a[x]]])	--max_c;
	--cnt[a[x]];
	++c_cnt[cnt[a[x]]];
}

int main()
{
	scanf("%d%d",&n,&m);
	forn(i,1,n)	scanf("%d",&a[i]);
	forn(i,1,m)	scanf("%d%d",&q[i].l,&q[i].r),q[i].id = i;
	sort(q + 1,q + m + 1,cmp);
	
	int l = 1,r = 0,maxv = 0;
	forn(_,1,m)
	{
		int ql = q[_].l,qr = q[_].r,pos = q[_].id;
		while(l > ql)	add(--l);
		while(r < qr)	add(++r);
		while(l < ql)	del(l++);
		while(r > qr)	del(r--);
		if(max_c <= (qr - ql + 2) / 2)	ans[pos] = 1;
		else	ans[pos] = 2 * max_c - (qr - ql + 1);
	}
	
	forn(_,1,m)	printf("%d\n",ans[_]);
    return 0;
}

E正在施工

posted @ 2021-04-21 19:59  随处可见的阿宅  阅读(79)  评论(0)    收藏  举报