CF #526 Div1 C Max Mex

大意

给一个n个点的树,树上的点上带有值,这些值为0到n-1的排列

Q个询问:

询问1.(参数点的坐标) 交换两个点上的值

询问2.(无参数) 在树上找一条简单路径,使该简单路径上MEX最大,输出该最大值

(2<=n<=200'000)

(1<=q<=200'000)

MEX: 返回集合中最小未出现的自然数 如MEX({0,1,2,4,8,16}) = 3

解法

线段树维护[v1 -> vn] 这一段是否可能 在一条简单路径上,如果可能,那么记录这条简单路径的端点坐标

保存和更新,利用线段树更新的log级别,和LCA 来进行树上线段的合并


关于合并 举例解释:

如果我们的线段树要合并[l1->r1][l2->r2] , 其中l2=r1+1

通过递归处理先得到

[l1->r1]这一段值,对应在树上的简单路径的端点坐标为vl1vr1

[l2->r2]这一段值,对应在树上的简单路径的端点坐标为vl2vr2

注意到 前序遍历 和 后续遍历的特征,如果 点A是B的祖先,那么前序遍历A<B, 且后序遍历A>B

那么有如果这4个端点,在从根到某个叶子的一条链上(当且仅当),则这四个端点的 前序遍历的偏序关系 正好 和后续遍历相反.

所以 我们可以通过对这4个点排序, 前序最小值对应的点 是否和 后续最大值对应的点 相同.如果相同,那么这4个点在一个链上返回[找到的最深点,最潜点] O(1)

以上是 存在从根下来的链经过4个端点.

下面考虑是否存在一个跨某节点连接 该节点两个子链的简单路径,经过4个端点

(假设dfs的子节点枚举从左向右,便于描述)

注意到,如果有祖先关系,那么前序的最大值为深度最深的点,如果是非祖先关系,那么 前序的最大值为最右侧的点,也就有了 前序最大是最右侧最深的点(假设叫RV)

对称的,后序遍历最小为最左侧最深的一个点.(假设叫LV)

那么 实际要考察的就是四个端点是否都在从LV到RV的这样一条简单路径上,

那么也就是LCA+判断点是否在链上,走一波O(log 树的深度)

如果失败,则用记录不可合并[any way -1也好 额外字段也好]


线段树更新,就日常更新就好了

查询,0是必定可以进链的

那么 尝试把0 和 (0-n/2) 左半合并(因为链合并有幂等性 不用考虑0和0自己冲突)

如果可以合并,就把合并后的和右半合并

如果不行,则 合并目标 = 合并目标的左半; O(log n)

相关知识

线段树(建立&更新&查询),老生常谈了[CF 入门必会],[相对于后缀数组,相对更难写,但能维护更多的状态]

合并树上的链(LCA,dfs 前序 后序遍历 特点,求一个点是否在一个树中的一条链上(类似LCA))

LCA, 求树上点的最近公共祖先:(基于fa[点i][幂次k] = 点i的2^k祖先),实现O(log(树高的))的时间复杂度

代码

code

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ten5 100000+10
#define MOD 1000000007
#define rep(i, a, n) for (int i=a;i<n;i++)
#define iif(c, t, f) ((c)?(t):(f))
#define per(i, a, n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second

const int N = 200010;
int n;
struct vertex {
int dep;
int v;
int _in;
int _out;
} i2v[N];
int v2i[N];

const int Log = 22;
int fa[N][Log + 1]; // fa[0][any] = 0 的性质很好
vector<int> child[N];

pair<int, int> T[N << 2];

void dfs(int i, int dep, int &idx) {
i2v[i].dep = dep;
i2v[i]._in = idx++;
for (auto item:child[i])
dfs(item, dep + 1, idx);
i2v[i]._out = idx++;
}

int Jump(int x, int d) {
if (d < 0)
return x;
per(i, 0, Log + 1){
if ((d >> i) & 1)
x = fa[x][i];
}
return x;
}

int LCA(int l, int r) {
int d = i2v[l].dep - i2v[r].dep;
if (d < 0) {
swap(l, r);
d = -d;
}
l = Jump(l, d);
if (l == r)
return l;
per(i, 0, Log + 1){
if (fa[l][i] != fa[r][i]) {
l = fa[l][i];
r = fa[r][i];
}
}
return fa[l][0];
}


bool onpath(int idx, int l_idx, int r_idx) {
int anc = LCA(l_idx, r_idx);
if (i2v[anc].dep > i2v[idx].dep)
return false;
return
Jump(l_idx, i2v[l_idx].dep - i2v[idx].dep) == idx ||
Jump(r_idx, i2v[r_idx].dep - i2v[idx].dep) == idx;
}

pair<int, int> mergeT(const pair<int, int> &v1, const pair<int, int> &v2) {
int arr[] = {v1.fi, v1.se, v2.fi, v2.se};
rep(i, 0, 4){
if (arr[i] == -1)
return {-1, -1};
}
int l_leaf = arr[0];
rep(i, 0, 4) {
if (i2v[arr[i]]._out < i2v[l_leaf]._out)
l_leaf = arr[i];
}

int r_leaf = arr[0];
rep(i, 0, 4) {
if (i2v[arr[i]]._in > i2v[r_leaf]._in)
r_leaf = arr[i];
}

if (l_leaf == r_leaf) { // 所有点在从根向下的一条链上
int rt = arr[0];
rep(i, 0, 4) {
if (i2v[arr[i]].dep < i2v[rt].dep)
rt = arr[i];
}
return {l_leaf, rt};
} else { // 链跨过了某节点连接某节点的两个子链
rep(i, 0, 4) {
if (!onpath(arr[i], l_leaf, r_leaf))
return {-1,-1};
}
}
return {l_leaf, r_leaf};
}

pair<int, int> initSeg(int o, int l, int r) {
if (l == r)
return T[o] = {v2i[l], v2i[r]};
int mid = (l + r) / 2;
return T[o] = mergeT(
initSeg(o << 1, l, mid),
initSeg(o << 1 | 1, mid + 1, r));
}

void updateSeg(int o, int l, int r, const int &val) {
if (l == r) {
T[o] = {v2i[val], v2i[val]};
return;
}
int mid = (l + r) / 2;
if (val <= mid)
updateSeg(o << 1, l, mid, val);
else
updateSeg(o << 1 | 1, mid + 1, r, val);
T[o] = mergeT(T[o << 1], T[o << 1 | 1]);
}

int query(int o, int l, int r, const pair<int, int> &now) {
if (l == r)
return mergeT(now, T[o]).fi == -1 ? l : l + 1;
int mid = (l + r) / 2;
auto tmp = mergeT(now, T[o << 1]);
if (tmp.fi == -1)
return query(o << 1, l, mid, now);
else
return query(o << 1 | 1, mid + 1, r, tmp);
}

int main() {
cin >> n;
rep(i, 1, n + 1) {
scanf("%d", &i2v[i].v);
v2i[i2v[i].v] = i;
}
rep(i, 2, n + 1) {
scanf("%d", &fa[i][0]);
child[fa[i][0]].pb(i);
}
rep(j, 1, Log + 1) {
rep(i, 1, n + 1) {
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}
int idx = 1;
dfs(1, 1, idx);

initSeg(1, 0, n - 1);

int Q;
cin >> Q;
while (Q--) {
int t;
scanf("%d", &t);
if (t == 1) {
int i, j;
scanf("%d %d", &i, &j);
swap(v2i[i2v[i].v], v2i[i2v[j].v]);
swap(i2v[i].v, i2v[j].v);
updateSeg(1, 0, n - 1, i2v[i].v);
updateSeg(1, 0, n - 1, i2v[j].v);
} else {
pair<int, int> st = {v2i[0], v2i[0]};
printf("%d\n", query(1, 0, n - 1, st));
}
}
return 0;
}

USACO4.4.1

输入

给你n(2<=n<=32)个点,m(0<=m<=1'000)条边,可包含重边,每条边权重c(0<=c<=2'000'000)

求/输出

最大流

让最小割被割的边数量最小,求该最小值

求在该最小值下,求字典序最小的最小割

解法

忽略0边[如果不忽略,可能在后面的问产生问题]

第一问最大流:最大流就增广路径

第二问:因为要求,让最小割的被割边的数量也最小,在进行最大流后,对所有满载且原始边不为0的边容量改为1,残余改为inf,再进行一次求最大流

第三问:在第二问基础上枚举删边,检查可能性,标记边.在原图上采取枚举删边,

关于此题

有看到这样的解法来处理二三问,把所有边按容量降序排列依次删边尝试,据说能过USACO,然而有反例[USACO数据真弱2333]

1
2
3
4
5
6
3 5
1 2 1
1 2 1
1 2 4
2 3 3
2 3 3

很明显总流量是6,要让最小割的数量最小是点2到点3 的两条流量为3的边


据说,有人的方案是把边容量乘1001再加上边index来做为新容量,从而最小边取%1001,最大流取/1001


测试数据里似乎没有c=0的时候


第二问的操作inf,必要性,简单的反例解释

1
2
3
4
5
6
7
8
4 7
1 2 7
2 3 7
1 3 2
3 4 2
3 4 2
3 4 2
3 4 2

明显1到3的流量可以达到9,而3到4的流量只有8

所以最小割一定是3到4,但如果耗尽改为1的话,且1到2,2到3都被耗尽,则有1到2,2到3都改为流量1,进行做第二问

如果不把1到3的2 改为inf,则会 认为最少的割边数量为3,而不是4


同样第二问结果并不能直接用于第三问,进行尝试删边,样例

1
2
3
4
5
6
4 5
1 2 4
2 3 2
2 4 2
1 3 100
3 4 2

最小割应该为 2->4 和3->4,如果1->2耗尽,在第二问变更后的图做尝试删边操作,则会发现1->2会影响流量,认为1->2属于割边

代码

code

EDU55E

输入

给你n(1<=n<=500'000)个数字(1<=a[i]<=500'000)和 一个目标值 c(1<=c<=500'000)

要求

任选连续一段加上任意值,使最后的c的出现次数最多,

输出

c最多出现的次数

解法

假设选的区间[l,r]是顶着头,或者 抵着尾的,那么和明显

ans= min(max{[0,i)出现k数字的次数}+[i,n)出现c的次数,max{[i,n)出现k数字的次数}+[0,i)出现c的次数)

用前缀和,可以O(n)做出,问题是无法解决 原数组1 2 1 目标是1的这种,只需要改中间的部分的.


想法1是 做分治

f(l,r) = max(fl(l,mid)+fr(mid,r),max(f(l,mid),f(mid,r)))

fl: c...c?...?

fr: ?...?c...c

也就是划分的mid是否 在选取的区间[l,r]内,问题是 看似分治了,但fl(l,mid)+fr(mid,r) 无法处理替换段是一样的情况,如果要处理时间复杂度不会够


之后想法是dp

因为可以观察到如果选取的值为v,那么整个数组上统计出现个数的时候采取的是形如

c…cv…vc…c的形状(其中每个形状均可以长度为0),那么也就是可以dp[state][i]来做,state分为第一段 第二段 第三段,

这样看上去是n*m的,但是 实际上当我们走到i的时候,只有a[i]的dp需要更改,所以是O(n)的


dp的整理

既然上面也观察到进行到i,只会影响到a[i]相关,那么可以把不同数字的都整合到一起,因为只会有当前a[i]对i位置的进行写和读(==c的会有其它读)

考虑形状

1
2
c...c?...?c...c
i

那么有 ans = max{ [0,i] 中前部分选c后部分选a[i]的最大次数+[i+1,n-1]c出现的次数 }

1
2
3
[0,i]中前部分选c后部分选a[i]的最大次数
= [0,i]中a[i]的次数 + max{[0,j]选c相对于选a[i]的增量} 其中0<=j<=i
= [0,i]中a[i]的次数 + max{[0,j]选c-[0,j]选a[i]} 其中0<=j<=i

至此上面皆可前缀和

1
2
3
4
5
6
7
8
9
i = 1 -> n:
sumc[i] = sumc[i-1] + (a[i] == c); // 前缀和,求到i个 有多少个c
sumci[i] = sumci[pre[a[i]]]+1; // 前缀和,求到i个 有多少个a[i]
maxder[i]=max(maxder[pre[a[i]]],sumc[i-1]-sumv[i]+1); // [0->j] 计算a[i] -> c变化的最大增量
pre[a[i]]=i; // pre记录a[i]上一次出现的位置
}
ans <- 0;
i = 0 -> n;
ans=max(ans,maxder[i]+sumci[i]+(sumc[n]-sumc[i])); // 增量+[0,i]中a[i]的个数+[i+1,n-1]中c的个数

代码

code

497D1B

输入

t个询问(1<=t<=100'000)

每个询问A,B,C (1<=A,B,C<=100'000)

要求

若a是A的因子,b是B的因子,c是C的因子,则(a,b,c)记为1种方案

(a,b,c),(a,c,b),(b,a,c),(b,c,a),(c,a,b),(c,b,a)视作同一种方案

输出

求总方案数

解法

看了别人超级宽的代码后 才看懂解法,虽然我第一眼也知道是个容斥

首先众所周知,预处理每个数的因子个数。

然后把A的因子个数 分为{A独有的因子的个数,仅AB共有的因子的个数,仅CA共有的因子的个数,ABC共有的因子的个数}

对B和C同样的处理,然后 就会发现,不过是个4*4*4的排列组合(因为不同的分化之间相互不重叠),注意去重,就随便写都过了,毕竟O(4*4*4*100'000)

497D1B

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,n) for (int i=a;i<n;i++)

int yzcnt[100010];

void init(){
rep(i,1,100001)
yzcnt[i]++;
rep(i,2,100001){
rep(j,1,100000/i+1){
if(i*j<100001){
yzcnt[i*j]++;
}
}
}
}

int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}

ll calc(ll *v1,ll *v2,ll *v3){
if(v1 == v2 && v2 == v3){
return (*v1)*(*v1+1)*(*v1+2)/6;
}
if(v1 == v2){
return (*v3)*((*v1+1)*(*v1)/2);
}
if(v2 == v3){
return (*v1)*((*v2+1)*(*v2)/2);
}
if(v3 == v1){
return (*v2)*((*v3+1)*(*v3)/2);
}
return (*v1)*(*v2)*(*v3);
}

int ec(int a,int b,int c){
return c+7*(b+7*a);
}

int main(){
init();
int t;
cin>>t;
while(t-->0){
int v[3];
scanf("%d %d %d",v,v+1,v+2);
int gab= gcd(v[0],v[1]);
int gbc= gcd(v[1],v[2]);
int gca= gcd(v[2],v[0]);
int gabc= gcd(gab,v[2]);

// 计算每一个的仅有的部分
/*0*/ll a = yzcnt[v[0]] - yzcnt[gab] - yzcnt[gca] + yzcnt[gabc];
/*1*/ll b = yzcnt[v[1]] - yzcnt[gbc] - yzcnt[gab] + yzcnt[gabc];
/*2*/ll c = yzcnt[v[2]] - yzcnt[gca] - yzcnt[gbc] + yzcnt[gabc];
/*3*/ll ab = yzcnt[gab] - yzcnt[gabc];
/*4*/ll bc = yzcnt[gbc] - yzcnt[gabc];
/*5*/ll ca = yzcnt[gca] - yzcnt[gabc];
/*6*/ll abc = yzcnt[gabc];

// 用于4*4*4的枚举
ll *slotA[] = {&a,&ab,&ca,&abc};
ll *slotB[] = {&b,&bc,&ab,&abc};
ll *slotC[] = {&c,&ca,&bc,&abc};

// 用于上面枚举的去重
int slotMaskA[] = {0,3,5,6};
int slotMaskB[] = {1,4,3,6};
int slotMaskC[] = {2,5,4,6};
bool masks[7*7*7];
rep(i,0,7*7*7){
masks[i] = false;
}

// 枚举
ll ans = 0;
rep(i,0,4){
rep(j,0,4){
rep(k,0,4){
int maskarr[] = {slotMaskA[i],slotMaskB[j],slotMaskC[k]};
sort(maskarr,maskarr+3);
int code = ec(maskarr[0],maskarr[1],maskarr[2]);
if(!masks[code]){// 去重
masks[code] = true;
ans +=calc(slotA[i],slotB[j],slotC[k]);
}
}
}
}
printf("%lld\n",ans);
}

return 0;
}

代码

497D1B

参考

493D1C 官方题解

493D1C Petr的代码

497D1B wavator超级宽的代码

493D1C

输入

给你n*n(1<=n<=1'000'000)的格子

要求

每个格子 图上 有3种颜色可以选择,

如果所有格子都上完色后, 存在一列或一行的颜色全部相同,则称作lucky

输出

求 所有lucky的方案数 modulo 998244353

解法

翻译自官方

A{i}表示 有i 行 颜色相同
B{i}表示 有i 列 颜色相同

然后直接 容斥原理公式(搜索一下就能找到的公式) 计算 $A_1 \cup A_2 \ldots A_n \cup B_1 \cup B_2 \ldots B_n$

然后因为 这些行列在哪里和我们的计算无关,所以对于A{i}可以看成选i行,让这i行每行内颜色相同,也就是C(n,i)倍的 方案数

所以有那个公式$ans = \sum_{i=0 \ldots n, j=0 \ldots n, i + j > 0} C_n^i C_n^j (-1)^{i + j + 1} f(i, j)]$

f(i,j)表示前i行j列 lucky的方案数

然后 具体表示f(i,j)

i,j其中一个有0的情况,这种情况,以行举例来说,同色的行 之间是可以不同色的,所以是$3^k$

$f(k, 0) = f(0, k) = 3^k \cdot 3^{n (n - k)}$

其它情况,这样的话 因为 如果至少有1行+1列是同色的,那么所有的同色的行列 都是同色的,这样就是3

$f(i, j) = 3 \cdot 3^{(n - i)(n - j)}$

很明显 这个n,肯定不是能做出来的

那么 分两块,ij带0O(n)时间内算出

其它部分 看数学操作了,首先 带入f(i,j)

$ans=\sum_{i=1}^{n}\sum_{j=1}^{n}C_n^iC_n^j(-1)^{i+j+1}3\cdot3^{(n-i)(n-j)}$

换元 $i \to n-i, j \to n - j$

$ans = 3 \sum_{i=0}^{n - 1}\sum_{j = 0}^{n - 1} C_n^{n - i} C_n^{n - j} (-1)^{n - i + n - j + 1} \cdot 3^{ij}$

等价化简

$ans = 3 \sum_{i=0}^{n - 1} \sum_{j = 0}^{n - 1} C_n^i C_n^j (-1)^{i + j + 1} \cdot 3^{ij}$

分离

$ans = 3 \sum_{i=0}^{n - 1} C_n^i (-1)^{i+1} \sum_{j = 0}^{n - 1} C_n^j (-1)^{j} \cdot (3^i)^j$

乘法分配率

$ans = 3 \sum_{i=0}^{n - 1} C_n^i (-1)^{i+1} \sum_{j = 0}^{n - 1} C_n^j (- 3^i)^j$

考虑对后面的求和简化,考虑下面式子

$(a + b)^n = \sum_{i = 0}^{n} C_n^i a^i b^{n - i}$

这里 我们把a取1,b取$-3^i$,再注意到求和到n不是n-1,进行一个加减消除,最后可以把ans化简为

$ans = 3 \sum_{i=0}^{n - 1} C_n^i (-1)^{i+1} [(1 + (-3^i))^n - (-3^i)^n]$

这个式子,可以累加求和在O(n)时间内做出了

众所周知的 模逆元 快速幂 取模什么的就不说了


感觉这里分类是我的问题,我自己想 始终把三个颜色分开想怎么计算,这里合在一起了。

497D1B

输入

t个询问(1<=t<=100'000)

每个询问A,B,C (1<=A,B,C<=100'000)

要求

若a是A的因子,b是B的因子,c是C的因子,则(a,b,c)记为1种方案

(a,b,c),(a,c,b),(b,a,c),(b,c,a),(c,a,b),(c,b,a)视作同一种方案

输出

求总方案数

解法

看了别人超级宽的代码后 才看懂解法,虽然我第一眼也知道是个容斥

首先众所周知,预处理每个数的因子个数。

然后把A的因子个数 分为{A独有的因子的个数,仅AB共有的因子的个数,仅CA共有的因子的个数,ABC共有的因子的个数}

对B和C同样的处理,然后 就会发现,不过是个4*4*4的排列组合(因为不同的分化之间相互不重叠),注意去重,就随便写都过了,毕竟O(4*4*4*100'000)

497D1B

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,n) for (int i=a;i<n;i++)

int yzcnt[100010];

void init(){
rep(i,1,100001)
yzcnt[i]++;
rep(i,2,100001){
rep(j,1,100000/i+1){
if(i*j<100001){
yzcnt[i*j]++;
}
}
}
}

int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}

ll calc(ll *v1,ll *v2,ll *v3){
if(v1 == v2 && v2 == v3){
return (*v1)*(*v1+1)*(*v1+2)/6;
}
if(v1 == v2){
return (*v3)*((*v1+1)*(*v1)/2);
}
if(v2 == v3){
return (*v1)*((*v2+1)*(*v2)/2);
}
if(v3 == v1){
return (*v2)*((*v3+1)*(*v3)/2);
}
return (*v1)*(*v2)*(*v3);
}

int ec(int a,int b,int c){
return c+7*(b+7*a);
}

int main(){
init();
int t;
cin>>t;
while(t-->0){
int v[3];
scanf("%d %d %d",v,v+1,v+2);
int gab= gcd(v[0],v[1]);
int gbc= gcd(v[1],v[2]);
int gca= gcd(v[2],v[0]);
int gabc= gcd(gab,v[2]);

// 计算每一个的仅有的部分
/*0*/ll a = yzcnt[v[0]] - yzcnt[gab] - yzcnt[gca] + yzcnt[gabc];
/*1*/ll b = yzcnt[v[1]] - yzcnt[gbc] - yzcnt[gab] + yzcnt[gabc];
/*2*/ll c = yzcnt[v[2]] - yzcnt[gca] - yzcnt[gbc] + yzcnt[gabc];
/*3*/ll ab = yzcnt[gab] - yzcnt[gabc];
/*4*/ll bc = yzcnt[gbc] - yzcnt[gabc];
/*5*/ll ca = yzcnt[gca] - yzcnt[gabc];
/*6*/ll abc = yzcnt[gabc];

// 用于4*4*4的枚举
ll *slotA[] = {&a,&ab,&ca,&abc};
ll *slotB[] = {&b,&bc,&ab,&abc};
ll *slotC[] = {&c,&ca,&bc,&abc};

// 用于上面枚举的去重
int slotMaskA[] = {0,3,5,6};
int slotMaskB[] = {1,4,3,6};
int slotMaskC[] = {2,5,4,6};
bool masks[7*7*7];
rep(i,0,7*7*7){
masks[i] = false;
}

// 枚举
ll ans = 0;
rep(i,0,4){
rep(j,0,4){
rep(k,0,4){
int maskarr[] = {slotMaskA[i],slotMaskB[j],slotMaskC[k]};
sort(maskarr,maskarr+3);
int code = ec(maskarr[0],maskarr[1],maskarr[2]);
if(!masks[code]){// 去重
masks[code] = true;
ans +=calc(slotA[i],slotB[j],slotC[k]);
}
}
}
}
printf("%lld\n",ans);
}

return 0;
}

代码

497D1B

参考

493D1C 官方题解

493D1C Petr的代码

497D1B wavator超级宽的代码

0%