cf round439

cf round439

A

题目要求

给出数组a和数组b 数组大小分别n
问存在多少对(i,j) 使得ai xor aj的值在这2n个数字中

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<bits/stdc++.h>
#define maxn 20050
using namespace std;
int a[maxn],b[maxn];
set<int>s;
int main(){
// freopen("input.txt","r",stdin);
int n;
scanf("%d",&n);
s.clear();
for(int i=1;i<=n;i++) scanf("%d",&a[i]),s.insert(a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]),s.insert(b[i]);
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s.count(a[i]^a[j])) ans++;
}
}
if(ans&1) puts("Koyomi");
else puts("Karen");
return 0;
}

思路

暴力
用set有n²logn的复杂度都能过 hash每一个数字把复杂度降到n²当然更快

B

题目要求

问b!/a!的末尾数字是多少 保证b>a

参考AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a,b;
int main(){
// freopen("input.txt","r",stdin);
scanf("%lld%lld",&a,&b);
LL c=b-a;
if(c>=10){
puts("0");
return 0;
}
int ans=1;
for(LL i=a+1;i<=b;i++){
ans*=i%10;
ans%=10;
}
printf("%d\n",ans);
return 0;
}

思路

只要b-a的大小超过10 末尾必然会乘上10 使得最后一位肯定0
如果小于10 暴力最后一个位置就行了

C

题目要求

有红 蓝 紫色三种颜色的点 给出三种颜色点的数量
可以在两个点之间连一条权值为1的边 但必须满足以下规则:
相同颜色的点不能连接 若连接必须确保他们之间的距离大于等于3
问一共有多少种连法 答案mod 998244353

参考AC代码

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
#include<bits/stdc++.h>
#define maxn 5050
#define mm(a,b) memset(a,b,sizeof(a))
#define LL long long
using namespace std;
const LL mod = 998244353;
LL pre[maxn];
LL co[maxn][maxn];
void init(){
pre[0]=pre[1]=1;
for(int i=2;i<=5000;i++) pre[i]=((pre[i-1]%mod)*i)%mod;
mm(co,0);
co[0][0]=1;
for(int i=0;i<=5000;i++){
co[i][0]=co[i][i]=1;
for(int j=1;j<=i;j++) co[i][j]=(co[i-1][j]+co[i-1][j-1])%mod;
}
}
LL solve(int x,int y){
LL ans=0;
for(int i=0;i<=x && i<=y;i++){
LL re=1;
re=(((re*co[x][i])%mod*co[y][i])%mod*pre[i])%mod;
ans=(ans+re)%mod;
}
return ans%mod;
}
int main(){
// freopen("input.txt","r",stdin);
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
init();
LL ans=1;
ans=(ans*solve(a,b))%mod;
ans=(ans*solve(a,c))%mod;
ans=(ans*solve(b,c))%mod;
printf("%lld\n",ans);
return 0;
}

思路

推到出公式为:∑k=0到min(a,b) C(a,k)C(b,k)k!
以上结果为ans1 换成a和c为ans2 换成b和c为ans3 答案就是ans1×ans2×ans3
预处理组合数取模和阶乘 可以降低复杂度
公式推导思路为:
三种颜色两两相连只有三种情况 考虑每种情况 相乘即可
假设现在是a和b 我们从a和b中取同样的k个(必须取相同的个数是为了保证可以一一对应相连) 所以这里k的上限为min(a,b)
再乘以k!是因为对应k!种连法 遍历k把这些情况加到一起就是第一种连法的总个数
这里的距离大于等于3等价于不存在两个点直接相连或者不存在某个点连接2个相同的颜色 所以得到k!种连法

E

题目要求

在一个n×m的方格板上,操作1将一个矩形区域的边界上加上一圈障碍,操作2将一个矩形区域的边界上的障碍移除,操作3询问两点是否能不越过障碍互相到达。题目保证任意两圈矩形障碍不会相交

参考AC代码(暴力差分)

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
#include<bits/stdc++.h>
#define maxn 2502
#define mm(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
int n,m,q;
int mp[maxn][maxn];
int main(){
// freopen("input.txt","r",stdin);
n=read(),m=read(),q=read();
mm(mp,0);
for(int k=1;k<=q;k++){
int op,x1,y1,x2,y2;
op=read(),x1=read(),y1=read(),x2=read(),y2=read();
if(op==1){
for(int i=x1;i<=x2;i++){
mp[i][y1]=k;
mp[i][y2+1]=-1;
}
}
else if(op==2){
for(int i=x1;i<=x2;i++) mp[i][y1]=mp[i][y2+1]=0;
}
else{
int st=0,ans1=0,ans2=0;
for(int i=y1;i>=0;i--){
if(mp[x1][i]>0){
if(st==0){
ans1=mp[x1][i];
break;
}
else st++;
}
else if(mp[x1][i]<0) st--;
}
st=0;
for(int i=y2;i>=0;i--){
if(mp[x2][i]>0){
if(st==0){
ans2=mp[x2][i];
break;
}
else st++;
}
else if(mp[x2][i]<0) st--;
}
if(ans1==ans2) puts("Yes");
else puts("No");
}
}
return 0;
}

参考AC代码(二维树状数组+hash)

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
#include<bits/stdc++.h>
#define LL long long
#define mm(a,b) memset(a,b,sizeof(a))
#define maxn 2550
using namespace std;
LL tree[maxn][maxn];
int n,m,q;
void add(int i,int j,LL x){
while(i<=n){
int jj=j;
while(jj<=m){
tree[i][jj]+=x;
jj+=jj&-jj;
}
i+=i&-i;
}
}
LL sum(int i,int j){
LL s=0;
while(i){
int jj=j;
while(jj){
s+=tree[i][jj];
jj-=jj&-jj;
}
i-=i&-i;
}
return s;
}
int main(){
// freopen("input.txt","r",stdin);
scanf("%d%d%d",&n,&m,&q);
mm(tree,0);
while(q--){
int op,x1,y1,x2,y2;
scanf("%d%d%d%d%d",&op,&x1,&y1,&x2,&y2);
LL ans=x1;
ans=ans*111+y1;
ans=ans*111+x2;
ans=ans*111+y2;
if(op==1){
add(x1,y1,ans);
add(x2+1,y2+1,ans);
add(x1,y2+1,-ans);
add(x2+1,y1,-ans);
}
else if(op==2){
add(x1,y1,-ans);
add(x2+1,y2+1,-ans);
add(x1,y2+1,ans);
add(x2+1,y1,ans);
}
else{
if(sum(x1,y1)==sum(x2,y2)) puts("Yes");
else puts("No");
}
}
return 0;
}

思路

问题转换为:二维坐标上有一些相互嵌套但不相交的矩形 每次询问2个点能否不越过这些矩形直接相连
思路1:对于1操作 每一行我们给出一个块号 并用差分的思想把起点和终点分别赋值为k和-1
对于2操作 我们只需要把这些行的起点终点清空
对于3操作 我们只需要找到2个点所在的块号ans1和ans2 若在同一个块 可以到达 否则不可以到达
注意这里找到块号的操作:第一个st为0 并且该值大于0的才是块号
注意用这种方法数组不能开大 以及要注意常数 写丑了会tle
思路2:我们每次hash一个数值来代表块号 然后用二维树状数组更新矩形里的数字 最后判断2个点的数字是否相等就可以判断是否为同一个块
只要确保hash的数值足够大就可以保证几乎不冲突

文章目录
  1. 1. cf round439
    1. 1.1. A
      1. 1.1.1. 题目要求
      2. 1.1.2. 参考AC代码
      3. 1.1.3. 思路
    2. 1.2. B
      1. 1.2.1. 题目要求
      2. 1.2.2. 参考AC代码
      3. 1.2.3. 思路
    3. 1.3. C
      1. 1.3.1. 题目要求
      2. 1.3.2. 参考AC代码
      3. 1.3.3. 思路
    4. 1.4. E
      1. 1.4.1. 题目要求
      2. 1.4.2. 参考AC代码(暴力差分)
      3. 1.4.3. 参考AC代码(二维树状数组+hash)
      4. 1.4.4. 思路
|