[CF1514] Codeforces Round #716 (Div. 2)

A. Perfectly Imperfect Array

题解

只要任意一个数不是平方数就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using namespace std;
int const maxn=2e5+10;
int main(){

int t,n,a,v;
cin>>t;
while(t--){
scanf("%d",&n);
bool flag=false;
for(int i=1;i<=n;i++){
scanf("%d",&a);
v=sqrt(a);
if(v*v!=a)flag=true;
}
if(flag)printf("YES\n");
else printf("NO\n");
}
return 0;
}

B . AND 0, Sum Big

题意

给你N和K, 问有多少长度为n的数组满足:

  • 所有元素在[0,\(2^k-1\) ]范围内
  • 所有元素AND值为0
  • 所有元素之和尽可能大

题解

快速幂:二进制k位,只要每一位有一个0就能满足条件2,为了满足条件3每一位只能在数组中的一个数中为0,所以每一位的选择有n个数,答案为\(n^k\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<bits/stdc++.h>
using namespace std;
long long p=1e9+7;
long long n,k,t;
long long pow_fast(long long a,long long b,long long mod){
long long num=1%mod;
for(;b;b>>=1,a=a*a%mod)
if(b&1)num=num*a%mod;
return num;
}
int main(){
cin>>t;
while(t--){
scanf("%lld%lld",&n,&k);
long long ans=pow_fast(n,k,p);
printf("%lld\n",ans);
}
return 0;
}

C . Product 1 Modulo N

题意

给定n,问[1,n-1]范围内,最多可以取多少数,使其乘积取模n为1

数据范围

\[\left( 2 \leq n \leq 10^{5}\right)\]

题解

首先知道一个结论:如果a,b的最大公约数g=gcd(a,b)不为1,则a%b大于1,因为a%b的余数一定是g的倍数。

(1) a%b = (a/g) % (b/g) * g

所以只能选与n互质的数,他们的乘积取余n的结果x也与n互质,x就在刚刚所取的数中,只要把x删掉就行了

证明1: a%b = (a/g) % (b/g) * g

证明:2:与b互质的数乘积取余b的余数与b互质

\(a_1,a_2,a_3..a_m\) 与b互质 z=a_1a_2a_3 z=n*b+p g=gcd(n,p) n=cg , p=dg z=gcb+dg=g(cb+d) 则 g是 z 的因子,如果g ≠ 1,则n与 z不互质,与题设矛盾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>
using namespace std;
vector<int> v;
int gcd(int a,int b)//最大公约数,辗转相除
{
return b?gcd(b,a%b):a;
}
int main(){
int n;
scanf("%d",&n);
v.push_back(1);
long long tmp=1;
for (long long i=2;i<n;i++){
if (gcd(i,n)==1){//只能选与n互质的数
v.push_back(i);
tmp=(1ll*tmp*i)%n;
}
}
if (tmp==1ll) cout<<v.size()<<endl;
else cout<<v.size()-1<<endl;
for (long long i=0;i<v.size();i++){
if (v[i]==tmp&&tmp!=1) continue;
cout<<v[i]<<" ";//余数>1时,把余数删
}
cout<<endl;
}

网上看到的另一种不错证明,原文链接

不能选与n不互质的数, 因为选了之后%n一定不为1,因为gcd(乘积,n)!=1. 证明: 设gcd(p,n)=z,则p=xz,n=yz, 设选的其他数乘积为q, 则pq%n=1,设pq=kn+1, 那么qxz=kyz+1, (qx-ky)z=1, 当且仅当z=1时式子才有可能成立, 而gcd=z>1,因此式子不可能成立, 所以如果选择了gcd!=1的数,一定不满足条件.

因此只能选与n互质的数, 设s为所有与n互质的数的积对n取模的结果, 如果s=1,那么这些数全部可以选择, 如果s!=1,此时s一定与n互质,因此s也在这些数中, 那么选除了s的其他数就行了

D. Cut and Stick

题意

给出长度为n的序列a,q个询问,每次询问[l,r]区间。

问至少把区间内的数重新组合成几段,才能满足每段众数的次数不超过 \(\left \lceil len \right \rceil\) , len为区间长度

数据范围

\(\left( 1 \leq n,q \leq 3*10^{5}\right)\)

\(a_{1}, a_{2, \ldots,} a_{n}\left(1 \leq a_{i} \leq n\right)\)

\((1 \leq l \leq r \leq n)\)

题解

  1. 如果初始区间众数个数< \(\left \lceil len \right \rceil\) , 则不用拆,答案为1。否则就要拆,关于每段如何拆分。

偶→奇+奇 ,可容纳数增加1;奇→偶+奇,可容纳数不变。于是每次就是尽量拆偶数,变化如下

初始偶→奇+奇→偶+奇+奇→奇+奇+奇+奇→偶+奇++奇+奇+奇...

初始奇→偶+奇→奇+奇+奇→偶+奇++奇+奇...

实际上就是初始为偶数多了一步,后面跟奇数一样拆法。每多1个,段数+2。

  1. 莫队求区间众数,时间复杂度n*sqrt(n)。开一个数组num记录每个数的个数,数组cou[x]记录数量为x的数有几个

每次区间增加时,增加这个数的个数,维护cou。还有判断增加后是否变成区间众数

区间变小时,减少这个数的个数,维护cou。看当前众数个数值的cou是否变为0,如果变为0说明当前数为唯一众数,众数个数应该-1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<bits/stdc++.h>
using namespace std;
// #define debug(x,i) cout<<"haha"<<i<<' '<<x<<endl;
int const MAXN = 3e5+7;
int a[MAXN],unit,n,m,nowmax,cnt[MAXN],ans[MAXN],num[MAXN];
struct query1{
int L,R,id;
}qu[MAXN];
bool cmp(query1 a,query1 b){
if(a.L/unit == b.L/unit) //相同块,对R排序
return a.R < b.R;
else return a.L/unit <b.L/unit; //不同块,对块排序
}
void mo_add(int x){
cnt[num[a[x]]]--;
num[a[x]]++;
cnt[num[a[x]]]++;
if(num[a[x]]>nowmax)nowmax=num[a[x]];
}
void mo_del(int x){
cnt[num[a[x]]]--;
num[a[x]]--;
cnt[num[a[x]]]++;
if(cnt[nowmax]==0)nowmax--;
}
void work(){
int L = 1,R = 0;
for(int i=0;i<m;i++){
while(R < qu[i].R){
R++;
mo_add(R);
}
while(R > qu[i].R){
mo_del(R);
R--;
}
while(L < qu[i].L){
mo_del(L);
L++;
}
while(L > qu[i].L){
L--;
mo_add(L);
}
// debug(nowmax,1);
if(nowmax<=ceil((R-L+1)/2.0))ans[qu[i].id]=1;
else{//奇偶不同拆法
int z=1,len=R-L+1,u=nowmax-ceil((R-L+1)/2.0);
if(len%2==0){z++;u--;}
while(u){
z+=2;u--;
}
ans[qu[i].id]=z;
}
}
}

int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
unit=sqrt(n);
for(int i=0;i<m;i++){ //读入查询
scanf("%d%d",&qu[i].L,&qu[i].R);
qu[i].id = i;
}
sort(qu,qu+m,cmp);
work();
for(int i=0;i<m;i++){
printf("%d\n",ans[i]);
}
return 0;
}

比赛时

想到了莫队,但是当莫队区间变小时卡住了,忘记了只要再维护一个众数的个数有多少就行了。