CodeForce刷题表

Day2

1602B - Divine Array

题意:

数组进行k次操作,询问第x个位置上的数是多少。

每次操作重置数组,新的a[i]等于a[i]在该数组中出现的次数

如: 2 1 1 4 3 1 2 ==> 2 3 3 1 1 3 2 ==> ……

思路:

可以发现数组其实是逐渐趋于统一的,相同的数一直都是相同的,不相同的数可能会变为相同的,当所有的数字都等于该数字出现的次数时,之后不再改变。

这样的话,最多操作n次(不是一个精确的值,实际上应该少于n)就会让他不再改变,对最多2000个数操作2000次,1e6时间复杂度是过得去的。

但是询问次数q = 1e5,如果每次询问都重新跑一遍,大概率要超时!所以就做一个预处理,询问时直接输出。

代码:

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int a[2005][2005];
int main(){
int t;
cin>>t;
while(t--) {
int n,q,x,k,real;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[0][i];
for(int i=1;i<=n;i++) {
int w[2005] = {0};
for(int j=1;j<=n;j++)
w[a[i-1][j]]++;
for(int j=1;j<=n;j++)
a[i][j]=w[a[i-1][j]];
}
cin>>q;
while(q--) {
cin>>x>>k;
cout<<a[min(k,n)][x]<<endl;
}
}
return 0;
}

总结:

有询问的题目要考虑询问时会不会超时,如果超时可能要考虑做一个预处理,操作完询问的题大概率要做预处理把。

1601A - Array Elimination

题意:

选择一个k,每次选择k个数,得到一个x = ai1 & ai2 & … & aik ,然后使所选的数都减去x,如果最后能使每一个数都等于0,就输出这个k,问满足条件的数都有哪些

思路:

(因为&操作后只剩下这k个数在二进制下都为1的位置,所以相当于把他们同为1的地方给删掉,也就是二进制的该位置上每次可以减去k个数,因此二进制下k必须是该位置的因子才可以完全去掉)

枚举二进制前30位(int范围内),记录该位置上有多少个1,如果没有1,那么任何数都不会对他产生影响,所有的计数器都+1;如果有1,那么把他的因子都+1。

结束之后如果某个数字的计数器==31 (任何数%1 == 0,所以a[1]实际上就是31),即他是每一个数的因子,那么他就可以做到把所有位置都变成0,因此输出这个数。

例如: 4 4 4 4 =>二进制 0100 0100 0100 0100 => 0 4 0 0 ,如果选一个数那么每次可以去掉0 1 0 0,选两个数每次可以去掉0 2 0 0,选三个数每次可以去掉0 3 0 0,选四个数每次可以去掉0 4 0 0,显然只有1 2 4满足题目要求。

代码:

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int a[N],cnt[N];
int main(){
int t;
cin>>t;
while(t--) {
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],cnt[i]=0;
for(int i=0;i<=30;i++) {
int t = 0;
for(int j=1;j<=n;j++)
if((1<<i)&a[j])
t++;
if(cnt==0)
for(int j=1;j<=n;j++)
cnt[j]++;
else
for(int j=1;j<=t;j++)
if(t%j==0)
cnt[j]++;
}
for(int i=1;i<=n;i++)
if(cnt[i] == cnt[1])
cout<<i<<" ";
cout<<endl;
}

return 0;
}

总结:

位运算的题最好是按照二进制的思想来解决,把每一个数都看作是二进制,这样比直接看作十进制简单不少!

1598B - Groups

题意:

有n个人,把n个人分成2组,要求每组在周一到周五任意一天全都有空(全是1),并且2组所选的日期不同。

思路:

假设一组是某一天A,然后另一组是另一天B。如果选定的一天A有空的不足一半,直接pass掉,如果超过了,那么就记录第A天和第B天都有空 以及 第A天没空第B天有空的。可以知道,最多有A有空的人-人数的一半或者是两天都有空的人(其中较小的一个),这些人就是可以为B提供贡献的,如果这个值 加上 第A天没空第B天有空的大于人数的一半,那么这存在,如果没有出现这样的情况,就是不存在。

代码:

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int a[1005][10];
int main(){
int t;
cin>>t;
while(t--) {
int n,f=1;
int q[10] = {0};
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=5;j++) {
cin>>a[i][j];
if(a[i][j])
q[j]++;
}
for(int i=1;i<=5&&f;i++) {
if(q[i]<n/2)
continue;
int last = q[i] - n/2;
for(int j=i+1;j<=5&&f;j++) {
int same=0,diff=0;
for(int k=1;k<=n&&f;k++) {
if(a[k][i] && a[k][j])
same++;
else if(!a[k][i] && a[k][j])
diff++;
int tmp = min(same,last);
if(tmp + diff >= n/2)
f = 0;
}
}
}
if(f)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}

return 0;
}

总结:

这也算半个数学题了吧,一定要动笔比划一下,不然真的好绕啊,想明白数值的关系其实就是一个暴力枚举。