A Task Computing
题意:给定长度为 n n n 的序列 { ( w i , p i ) } \{(w_i,p_i)\} {(wi,pi)},从中选出 m m m 项并重新排列得到子序列 { a 1 , a 2 , ⋯ , a m } \{a_1,a_2,\cdots,a_m\} {a1,a2,⋯,am},最大化 ∑ i = 1 m w a i ∏ j = 1 i p a j \displaystyle \sum_{i=1}^m w_{a_i} \prod_{j=1}^i p_{a_j} i=1∑mwaij=1∏ipaj。 n ≤ 1 × 1 0 5 n \leq 1\times 10^5 n≤1×105, m ≤ 20 m \leq 20 m≤20, p i ∈ [ 0.8 , 1.2 ] p_i \in [0.8,1.2] pi∈[0.8,1.2]。
解法:如果选出的元素给定为子序列 { ( w i , p i ) } \{(w_i,p_i)\} {(wi,pi)},考虑如何贪心。使用常见的相邻局部调整法证明贪心:交换相邻两个元素 ( w i , p i ) (w_i,p_i) (wi,pi) 与 ( w i + 1 , p i + 1 ) (w_{i+1},p_{i+1}) (wi+1,pi+1),对最终答案的影响只会与 w i w_{i} wi 与 w i + 1 w_{i+1} wi+1 的系数有关。原始系数为 a = w i ∏ j = 1 i p j + w i + 1 ∏ j = 1 i + 1 p j a=\displaystyle w_i\prod_{j=1}^{i} p_j+w_{i+1}\prod_{j=1}^{i+1}p_j a=wij=1∏ipj+wi+1j=1∏i+1pj,改变后系数为 b = w i + 1 p i ∏ j = 1 i + 1 p j + w i ∏ j = 1 i + 1 p j b=\displaystyle \dfrac{w_{i+1}}{p_i}\prod_{j=1}^{i+1} p_j+w_{i}\prod_{j=1}^{i+1}p_j b=piwi+1j=1∏i+1pj+wij=1∏i+1pj。令 a − b = ( ∏ j = 1 i − 1 p j ) ( w i p i + w i + 1 p i p i + 1 − w i + 1 p i + 1 − w i p i p i + 1 ) > 0 \displaystyle a-b=\left(\prod_{j=1}^{i-1}p_j\right)(w_ip_i+w_{i+1}p_ip_{i+1}-w_{i+1}p_{i+1}-w_ip_ip_{i+1})>0 a−b=(j=1∏i−1pj)(wipi+wi+1pipi+1−wi+1pi+1−wipipi+1)>0,则 w i ( p i + 1 − 1 ) < w i + 1 ( p i − 1 ) w_i(p_{i+1}-1)<w_{i+1}(p_i-1) wi(pi+1−1)<wi+1(pi−1)。因而贪心规则为 w i ( p i + 1 − 1 ) w_i(p_{i+1}-1) wi(pi+1−1)。
按照这一贪心排序,那么选择的元素必然按此顺序。为了便于计算,可以用秦九韶算法对上式进行倒序计算。设 f i , j f_{i,j} fi,j 为倒序遍历到第 i i i 个元素,已经选择了 j j j 个元素的最大值,则有 f i , j ← max ( f i + 1 , j , f i + 1 , j − 1 p i + w i ) f_{i,j} \leftarrow \max(f_{i+1,j},f_{i+1,j-1}p_i+w_i) fi,j←max(fi+1,j,fi+1,j−1pi+wi)。总时间复杂度 O ( n log n + n m ) \mathcal O(n \log n+nm) O(nlogn+nm)。
#include <bits/stdc++.h>
using namespace std;
const long double inf = 1e12;
struct node
{
long long w;
long double p;
bool operator<(const node &b)const
{
return w * (b.p - 1) < b.w * (p - 1);
}
};
const int N = 100000;
node que[N + 5];
long double f[N + 5][25];
int main()
{
int n, m, p;
long long w;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n;i++)
scanf("%lld", &que[i].w);
for (int i = 1; i <= n;i++)
{
scanf("%d", &p);
que[i].p = 1.0 * p / 10000;
}
sort(que + 1, que + n + 1);
f[n + 1][0] = 0;
for (int i = 1; i <= m; i++)
f[n + 1][i] = -inf;
for (int i = n; i >= 0; i--)
for (int j = 0; j <= m; j++)
{
f[i][j] = f[i + 1][j];
if (j)
f[i][j] = max(f[i][j], que[i].w + que[i].p * f[i + 1][j - 1]);
}
printf("%.12Lf\n", f[1][m]);
return 0;
}
B 2D Internet Angel
题意:给定内外同心圆,半径分别为 R 1 , R 2 R_1,R_2 R1,R2,沿内侧圆外有一 n n n 边外接多边形 { P i } \{P_i\} {Pi},与内侧圆的切点为 { Q i } \{Q_i\} {Qi},保证这一多边形完全在外侧圆内。记此外接多边形外与外侧圆内侧部分的点到多边形和内侧圆切点的最近距离为 X X X,求 E ( X 2 ) E(X^2) E(X2)。 n ≤ 1 × 1 0 5 n \leq 1\times 10^5 n≤1×105。
解法:过凸包顶点
P
i
(
x
i
,
y
i
)
P_i(x_i,y_i)
Pi(xi,yi) 和
P
i
+
1
(
x
i
+
1
,
y
i
+
1
)
P_{i+1}(x_{i+1},y_{i+1})
Pi+1(xi+1,yi+1) 的中点
Q
i
Q_{i}
Qi,和外侧圆弧(即
F
P
2
Q
2
E
FP_2Q_2E
FP2Q2E)所包络区域内的点的最近距离是到
Q
i
Q_i
Qi 点的距离。记
P
i
P_i
Pi 极角为
β
i
\beta_i
βi,
Q
i
Q_i
Qi 极角为
α
i
\alpha_i
αi,设一点位于
U
(
x
,
y
)
U(x,y)
U(x,y),则利用
Δ
U
O
Q
\Delta UOQ
ΔUOQ 的余弦定理,设
∠
P
i
O
U
=
θ
\angle P_iOU=\theta
∠PiOU=θ,
∠
U
O
Q
i
=
α
i
−
β
i
−
θ
\angle UOQ_i=\alpha_i-\beta_i-\theta
∠UOQi=αi−βi−θ,则有
U
Q
2
2
=
O
U
2
+
O
Q
2
2
−
2
O
U
⋅
O
Q
2
cos
(
α
i
−
β
i
−
θ
)
UQ_2^2=OU^2+OQ_2^2-2OU\cdot OQ_2 \cos(\alpha_i-\beta_i-\theta)
UQ22=OU2+OQ22−2OU⋅OQ2cos(αi−βi−θ),因而只与
O
U
=
r
OU=r
OU=r 和极角
θ
\theta
θ 有关,因而可以利用极坐标下二重积分,计算出对于
Q
i
Q_i
Qi 答案为:
g
i
=
2
∫
0
α
i
−
β
i
d
θ
∫
R
1
sec
(
α
i
−
β
i
−
θ
)
R
2
(
R
1
2
+
r
2
−
2
R
1
r
cos
(
α
i
−
β
i
−
θ
)
)
r
d
r
g_i=2\int_{0}^{\alpha_i-\beta_i} {\rm d}\theta\int_{R_1\sec (\alpha_i-\beta_i-\theta)}^{R_2} (R_1^2+r^2-2R_1r\cos (\alpha_i-\beta_i-\theta))r{\rm d}r
gi=2∫0αi−βidθ∫R1sec(αi−βi−θ)R2(R12+r2−2R1rcos(αi−βi−θ))rdr
二倍的原因在于 ∠ P i O Q i = ∠ Q i O P i + 1 \angle P_iOQ_i=\angle Q_iOP_{i+1} ∠PiOQi=∠QiOPi+1。
考虑计算如下的二重积分:
f
(
α
)
=
∫
0
α
d
θ
∫
R
1
sec
θ
R
2
(
R
1
2
+
r
2
−
2
R
1
r
cos
θ
)
r
d
r
=
∫
0
α
d
θ
(
R
1
2
r
2
2
+
1
4
r
4
−
2
R
1
r
3
3
cos
θ
)
∣
R
1
sec
θ
R
2
=
α
(
R
1
2
R
2
2
2
+
1
4
R
2
4
)
−
R
1
4
4
∫
0
α
2
sec
2
θ
+
sec
4
θ
d
θ
−
2
R
1
R
2
3
3
∫
0
α
cos
θ
d
θ
+
2
R
1
4
3
∫
0
α
sec
2
θ
d
θ
=
α
(
R
1
2
R
2
2
2
+
1
4
R
2
4
)
−
R
1
4
tan
α
12
cos
2
α
−
2
3
R
1
R
2
3
sin
α
\begin{aligned} f(\alpha)=&\int_{0}^{\alpha}{\rm d}\theta\int_{R_1\sec\theta}^{R_2} (R_1^2+r^2-2R_1r\cos \theta)r{\rm d}r\\ =&\int_{0}^{\alpha}{\rm d}\theta \left .\left(\dfrac{R_1^2r^2}{2}+\dfrac{1}{4}r^4-\dfrac{2R_1r^3}{3}\cos \theta\right)\right |_{R_1\sec \theta}^{R_2}\\ =&\alpha\left(\dfrac{R_1^2R_2^2}{2}+\dfrac{1}{4}R_2^4\right)-\dfrac{R_1^4}{4}\int_{0}^{\alpha}2\sec^2 \theta+\sec^4 \theta{\rm d}\theta-\dfrac{2R_1R_2^3}{3}\int_{0}^{\alpha}\cos \theta{\rm d}\theta+\dfrac{2R_1^4}{3}\int_{0}^{\alpha}\sec^2 \theta{\rm d}\theta\\ =&\alpha\left(\dfrac{R_1^2R_2^2}{2}+\dfrac{1}{4}R_2^4\right)-\dfrac{R_1^4\tan \alpha}{12\cos^ 2\alpha}-\dfrac{2}{3}R_1R_2^3\sin \alpha \end{aligned}
f(α)====∫0αdθ∫R1secθR2(R12+r2−2R1rcosθ)rdr∫0αdθ(2R12r2+41r4−32R1r3cosθ)
R1secθR2α(2R12R22+41R24)−4R14∫0α2sec2θ+sec4θdθ−32R1R23∫0αcosθdθ+32R14∫0αsec2θdθα(2R12R22+41R24)−12cos2αR14tanα−32R1R23sinα
因而可以
O
(
1
)
\mathcal O(1)
O(1) 的计算出每个
Q
i
Q_i
Qi 的答案。最后只需要使用直线与直线的交算出
P
i
P_i
Pi,得到凸多边形
C
=
{
P
n
}
C=\{P_n\}
C={Pn} 然后求出其面积
S
S
S,答案为
∑
i
=
1
n
g
i
π
R
2
2
−
S
C
\displaystyle \dfrac{\sum_{i=1}^n g_i}{\pi R_2^2-S_C}
πR22−SC∑i=1ngi。
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d%Lf%Lf", &n, &r1, &r2);
vector<Point> Q(n), P(n);
vector<int> angQ(n);
for (int i = 0, x; i < n;i++)
{
scanf("%d", &x);
angQ[i] = x;
Q[i].x = r1 * cosl(x * PI / 10000);
Q[i].y = r1 * sinl(x * PI / 10000);
}
for (int i = 0; i < n;i++)
{
Line now;
now.p = Q[i];
now.v = (Point){-Q[i].y, Q[i].x};
Line nx;
nx.p = Q[(i + 1) % n];
nx.v = (Point){-Q[(i + 1) % n].y, Q[(i + 1) % n].x};
P[i] = nx.inter(now);
}
auto inner = convexhull(P);
auto cal_ang = [&](int id)
{
return atan2l(P[id].y, P[id].x);
};
long double ans = 0;
for (int i = 0; i < n;i++)
{
long double nowQ = angQ[i] * PI / 10000;
long double prevP = cal_ang((i + n - 1) % n);
long double nowP = cal_ang(i);
while(prevP > nowQ)
prevP -= PI;
while(nowP < nowQ)
nowP += PI;
ans += f(nowP - nowQ) + f(nowQ - prevP);
}
printf("%.15Lf\n", ans / (r2 * r2 * PI - inner.area() / 2));
}
return 0;
}
C Easy Counting Problem
题意:给定字符集大小 Σ \Sigma Σ,和每个字符出现次数的下限 { c i } \{c_i\} {ci}, q q q 次询问由 Σ \Sigma Σ 中字符构成的长度为 n n n 的字符串的个数。 q ≤ 300 q \leq 300 q≤300, n ≤ 1 × 1 0 7 n \leq 1\times 10^7 n≤1×107, ∑ c i ≤ 5 × 1 0 4 \sum c_i \leq 5\times 10^4 ∑ci≤5×104, ∣ Σ ∣ ≤ 10 |\Sigma| \leq 10 ∣Σ∣≤10。
解法:对于
∑
c
i
\sum c_i
∑ci 较小的约束条件,可以考虑使用生成函数。本题中由于有次数限制并且对排列计数,因而使用 EGF 指数生成函数。对于第
i
i
i 个字符,其方案对应的 EGF 为
∑
j
=
c
i
+
∞
x
j
j
!
=
e
x
−
∑
j
=
0
c
i
−
1
x
j
j
!
\sum_{j=c_i}^{+\infty} \dfrac{x^j}{j!}=e^x-\sum_{j=0}^{c_i-1} \dfrac{x^j}{j!}
j=ci∑+∞j!xj=ex−j=0∑ci−1j!xj
因而对于给定长度的
n
n
n,其答案为
n
!
[
x
n
]
∏
i
=
1
∣
Σ
∣
∑
j
=
c
i
+
∞
x
j
j
!
\displaystyle n![x^n]\prod_{i=1}^{|\Sigma|} \sum_{j=c_i}^{+\infty}\dfrac{x^j}{j!}
n![xn]i=1∏∣Σ∣j=ci∑+∞j!xj。考虑如何快速计算:
∏
i
=
1
∣
Σ
∣
∑
j
=
c
i
+
∞
x
j
j
!
=
∏
i
=
1
∣
Σ
∣
(
e
x
−
∑
j
=
0
c
i
−
1
x
j
j
!
)
=
∑
i
=
0
∣
Σ
∣
(
−
1
)
∣
Σ
∣
−
i
e
i
x
(
∏
j
∈
S
,
∣
S
∣
=
∣
Σ
∣
−
i
∑
k
=
0
c
j
−
1
x
k
k
!
)
\begin{aligned} \prod_{i=1}^{|\Sigma|} \sum_{j=c_i}^{+\infty}\dfrac{x^j}{j!}=&\prod_{i=1}^{|\Sigma|}\left(e^x-\sum_{j=0}^{c_i-1}\dfrac{x^j}{j!}\right)\\ =&\sum_{i=0}^{|\Sigma|}(-1)^{|\Sigma|-i}e^{ix} \left(\prod_{j \in S,|S|=|\Sigma|-i} \sum_{k=0}^{c_j-1}\dfrac{x^k}{k!}\right) \end{aligned}
i=1∏∣Σ∣j=ci∑+∞j!xj==i=1∏∣Σ∣(ex−j=0∑ci−1j!xj)i=0∑∣Σ∣(−1)∣Σ∣−ieix
j∈S,∣S∣=∣Σ∣−i∏k=0∑cj−1k!xk
其中
S
∈
Σ
S \in \Sigma
S∈Σ。但是直接计算上式是需要计算
O
(
∣
Σ
∣
2
∣
Σ
∣
)
O(|\Sigma|2^{|\Sigma|})
O(∣Σ∣2∣Σ∣) 次多项式相乘,不够优秀。但是注意到这一形式与 01 背包非常相似,因而使用背包形式的转移:
f
i
,
j
f_{i,j}
fi,j 表示截止前
i
i
i 个字符,已经乘了
j
j
j 个多项式的多项式。其转移形式为
f
i
,
j
←
f
i
−
1
,
j
+
f
i
−
1
,
j
−
1
g
(
j
)
f_{i,j} \leftarrow f_{i-1,j}+f_{i-1,j-1}g(j)
fi,j←fi−1,j+fi−1,j−1g(j),其中
g
(
j
)
=
∑
k
=
0
c
j
−
1
x
k
k
!
\displaystyle g(j)=\sum_{k=0}^{c_j-1}\dfrac{x^k}{k!}
g(j)=k=0∑cj−1k!xk。 这样只需要计算
O
(
∣
Σ
∣
2
)
\mathcal O(|\Sigma|^2)
O(∣Σ∣2) 次多项式乘法计算。
因而预处理出 f ∣ Σ ∣ , i , i ∈ [ 0 , ∣ Σ ∣ ] \displaystyle f_{|\Sigma|,i},i \in [0,|\Sigma|] f∣Σ∣,i,i∈[0,∣Σ∣] 后,容易注意到 g g g 的次数仅为 ∑ c i \sum c_i ∑ci。对于第 n n n 项,其答案来源为 e ( ∣ Σ ∣ − i ) x e^{(|\Sigma|-i)x} e(∣Σ∣−i)x 的第 n − k n-k n−k 项与 f ∣ Σ ∣ , i f_{|\Sigma|,i} f∣Σ∣,i 的第 k k k 项的系数乘积和,即 ∑ k = 0 ∑ c i ∑ i = 0 ∣ Σ ∣ ( − 1 ) ∣ Σ ∣ − i n ! ( ∣ Σ ∣ − i ) n − k ( n − k ) ! ( [ x k ] f ∣ Σ ∣ , i ) \displaystyle \sum_{k=0}^{\sum c_i}\sum_{i=0}^{|\Sigma|}(-1)^{|\Sigma|-i}\dfrac{n!(|\Sigma|-i)^{n-k}}{(n-k)!} ([x^k]f_{|\Sigma|,i}) k=0∑∑cii=0∑∣Σ∣(−1)∣Σ∣−i(n−k)!n!(∣Σ∣−i)n−k([xk]f∣Σ∣,i)。
因而总的复杂度为 O ( ∣ Σ ∣ 2 ∑ c i log ∑ c i + q ∣ Σ ∣ ∑ c i ) \mathcal O(|\Sigma|^2 \sum c_i \log \sum c_i+q|\Sigma| \sum c_i) O(∣Σ∣2∑cilog∑ci+q∣Σ∣∑ci)。
void Solve() {
scanf("%d", &m);
vector<Poly> p(m + 1);
p[0] = {1};
for (int i = 0, x; i < m; ++i) {
scanf("%d", &x);
Poly c = Poly(ifac, ifac + x);
for (int j = i + 1; j; --j)
p[j] += p[j - 1] * c;
}
scanf("%d", &q);
while (q--) {
int n, ans = 0;
scanf("%d", &n);
if (n < p[m].size()) {
puts("0");
continue;
}
for (int k = 0; k <= m; ++k) {
int res = 0;
for (int i = (int)p[k].size() - 1, pw = POW(m - k, n - i); ~i; --i) {
res = (res + (ll)p[k][i] * pw % P * ifac[n - i]) % P;
pw = (ll)pw * (m - k) % P;
}
inc(ans, k & 1 ? P - res : res);
}
printf("%lld\n", (ll)ans * fac[n] % P);
}
}
D、E Jobs
题意:有 n n n 家公司,第 i i i 家公司有 m i m_i mi 个工作,对能力要求为 ( a j , b j , c j ) (a_j,b_j,c_j) (aj,bj,cj),其中 1 ≤ a j , b j , c j ≤ 400 1 \leq a_j,b_j,c_j \leq 400 1≤aj,bj,cj≤400。若候选者能力值为 ( x , y , z ) (x,y,z) (x,y,z) 满足有一个工作符合 x ≥ a j , y ≥ b j , z ≥ c j x \geq a_j,y \geq b_j,z \geq c_j x≥aj,y≥bj,z≥cj,则认为该候选者可以被该公司录用。 q q q 次询问,每次给定一个候选者的能力值,问能被几个公司录用。D 题 $ n \leq 10$,E 题 n ≤ 1 × 1 0 6 n \leq 1\times 10^6 n≤1×106, q ≤ 2 × 1 0 6 q \leq 2\times 10^6 q≤2×106。
解法:显然一个朴素思路为维护第 i i i 家公司对前两个能力要求分别为 x , y x,y x,y 时,最少需要多少的 z z z 才能录用。这样进行二维前缀和即可,复杂度 O ( n m 2 + q ) \mathcal O(nm^2+q) O(nm2+q)。但是这样无法通过 E 题。
考虑如果只有两维的能力值 ( a i , b i ) (a_i,b_i) (ai,bi) 要求,考虑二位数点模型。首先根据 a i a_i ai 从小到大排序,把完全包络的职位( a i ≤ a j , b i ≤ b j a_i \leq a_j,b_i \leq b_j ai≤aj,bi≤bj)删除,则一定可以得到如下的阶梯状前缀和。
为了让一个公司对前缀和的影响至多只有 1 1 1——即一个公司如果有多个职位对候选者符合,在前缀和矩阵上的值也只有 1 1 1,绿点表示增加 1 1 1,红点表示减少 1 1 1,下方的矩形右上端点为 ( a i , b i ) (a_i,b_i) (ai,bi)。灰色区域表示该公司可以对哪些候选者发出 offer。记录该数组为 p i , j p_{i,j} pi,j。
考虑插入和删除一个职位会发生什么,以中间的矩形 ( a i , b i ) (a_i,b_i) (ai,bi) 为例。
- 插入: p a i , b i p_{a_i,b_i} pai,bi 增加, p a i , b i − 1 p_{a_i,b_{i-1}} pai,bi−1 减少, p a i + 1 , b i p_{a_{i+1},b_i} pai+1,bi 减少。
- 删除: p a i , b i p_{a_i,b_i} pai,bi 减少, p a i , b i − 1 p_{a_i,b_{i-1}} pai,bi−1 和 p a i + 1 , b i p_{a_{i+1},b_i} pai+1,bi 补回来。
其操作类似于链表。
接下来考虑第三维的影响。对每个职位按照 c i c_i ci 从小到大进行排序,然后考虑分层进行,对于每一层内部和回归到这一二维问题,逐层职位堆叠起来。加入第三维且按从小到大顺序之后,对于第 i i i 个职位,考虑类似单调队列的想法:
- 若已经存在的矩形(职位)满足 a j ≥ a i a_j \geq a_i aj≥ai 且 b j ≥ b i b_j \geq b_i bj≥bi,则对于 z ≥ c i z \geq c_i z≥ci 的人, j j j 工作已经完全被 i i i 顶死了,弹出 j j j;
- 若存在矩形满足 a j ≤ a i a_j \leq a_i aj≤ai 且 b j ≤ b i b_j \leq b_i bj≤bi,则当前这个职位被 j j j 职位顶掉了,不需要加入。
因而对于一家公司,可以 O ( m log m ) \mathcal O(m \log m) O(mlogm) 的预处理出 p p p 数组,然后进行 O ( c 3 ) \mathcal O(c^3) O(c3) 的前缀和即可。每次询问可以做到 O ( 1 ) \mathcal O(1) O(1),总复杂度 O ( n m log m + c 3 + q ) \mathcal O(nm \log m+c^3+q) O(nmlogm+c3+q)。
void dele(multiset<hh>::iterator pos,int h){
--pre[pos->a][pos->b][h];
if(pos==st.begin()){
if(pos!=--st.end()){
it1=pos,++it1;
++pre[it1->a][pos->b][h];
}
}
else if(pos==--st.end()){
it1=pos,--it1;
++pre[pos->a][it1->b][h];
}
else{
it1=it2=pos,--it1,++it2;
++pre[pos->a][it1->b][h],
++pre[it2->a][pos->b][h],
--pre[it2->a][it1->b][h];
}
st.erase(pos);
}
void ins(hh x){
it=st.insert(x);
++pre[x.a][x.b][x.c];
if(it==st.begin()){
if(++it!=st.end()) --pre[it->a][x.b][x.c];
}
else if(it==--st.end()){
if(it!=st.begin()) --it,--pre[x.a][it->b][x.c];
}
else{
it1=it,--it1,it2=it,++it2;
++pre[it2->a][it1->b][x.c];
--pre[it2->a][it->b][x.c];
--pre[it->a][it1->b][x.c];
}
}
IL int chk(hh x){
it=st.upper_bound(x);
if(it!=st.end()){if(it->b>=x.b){dele(it,x.c);return 0;}}
if(it!=st.begin()){if((--it)->b<=x.b){flag=0;return 1;}}
return 1;
}
int main()
{
n=in(),q=in();
for(int i=1;i<=n;++i){
int k=in();
for(int j=1;j<=k;++j)
a[j]=(hh){in(),in(),in()};
st.clear(),sort(a+1,a+k+1,cmp1);
for(int j=1;j<=k;++j){
flag=1;while(!chk(a[j]));
if(flag) ins(a[j]);
}
}
for(int i=1;i<=M;++i)
for(int j=0;j<=M;++j)
for(int k=0;k<=M;++k)
pre[i][j][k]+=pre[i-1][j][k];
for(int i=0;i<=M;++i)
for(int j=1;j<=M;++j)
for(int k=0;k<=M;++k)
pre[i][j][k]+=pre[i][j-1][k];
for(int i=0;i<=M;++i)
for(int j=0;j<=M;++j)
for(int k=1;k<=M;++k)
pre[i][j][k]+=pre[i][j][k-1];
int ans=0,lastans=0,seed=in();
std::mt19937 rng(seed);
std::uniform_int_distribution<> u(1,400);
while(q--){
int IQ=(u(rng)^lastans)%400+1;
int EQ=(u(rng)^lastans)%400+1;
int AQ=(u(rng)^lastans)%400+1;
ans=(1ll*ans*seed%p+(lastans=pre[IQ][EQ][AQ]))%p;
}
printf("%d\n",ans);
return 0;
}
H Wall Builder II
题意:给定 n n n,长度为 1 × i 1\times i 1×i 的砖块有 n − i + 1 n-i+1 n−i+1 个。现将这些砖拿来砌墙,不允许旋转(只能 1 × i 1\times i 1×i 的放置)且要密铺,且要求砌成的墙为矩形,问这个大矩形的最小周长。 n ≤ 100 n \leq 100 n≤100。
解法:面积 S = ∑ i = 1 n i ( n − i + 1 ) \displaystyle S=\sum_{i=1}^n i(n-i+1) S=i=1∑ni(n−i+1)。因而枚举长宽 h , w h,w h,w,贪心的从大到小的将 1 × w 1\times w 1×w 的区域填砖块,填满 h h h 次即可。可以证明这样贪心对 n ≤ 100 n \leq 100 n≤100 成立。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t, n;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
int s = 0;
for (int i = 1; i <= n;i++)
s += i * (n - i + 1);
vector<pair<pair<int, int>, pair<int, int>>> put;
function<bool(int, int)> check = [&](int w, int h)
{
put.clear();
vector<int> cnt(n + 1);
for (int i = 1; i <= n;i++)
cnt[i] = n - i + 1;
for (int times = 0; times < h; times++)
{
int last = w;
while(last)
{
bool flag = 0;
for (int j = min(n, last); j >= 1;j--)
if(cnt[j])
{
put.emplace_back(make_pair(times, w - last), make_pair(times + 1, w - last + j));
flag = 1;
last -= j;
cnt[j]--;
break;
}
if (!flag)
return false;
}
}
return true;
};
int ans = 0;
for (int i = sqrt(s); i >= 1;i--)
if (s % i == 0 && (check(i, s / i) || check(s / i, i)))
{
ans = 2 * (i + s / i);
break;
}
printf("%d\n", ans);
for (auto i : put)
printf("%d %d %d %d\n", i.first.second, i.first.first, i.second.second, i.second.first);
}
return 0;
}
J Counting Fish Again
题意:在第一象限含坐标轴的无穷大方格纸上,可以画一条斜率为 − 1 -1 −1 的线段 ( u 1 , v 1 ) → ( u 2 , v 2 ) (u_1,v_1) \to (u_2,v_2) (u1,v1)→(u2,v2),或者擦除已经有的一条线段,问每次操作后鱼的个数。鱼形状如下:
操作次数 q q q 满足 q ≤ 1 × 1 0 6 q \leq 1\times 10^6 q≤1×106。
解法:对于每一个截距的直线单独考虑,利用珂朵莉树的方法维护每条直线上的最长连续线段,只需要对每个最长连续线段所对应的鱼进行计数即可。注意到鱼尾和鱼身的连接点可以唯一确定一条鱼,因而对这一连接点进行讨论。
首先考虑鱼头朝下的情况。这种情况复杂在于鱼头可能会超出坐标格。假定鱼尾和鱼身的连接点 P P P 为 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),此时所画斜线方程为 l : x + y = B l:x+y=B l:x+y=B,斜线线段为 ( u 1 , v 1 ) → ( u 2 , v 2 ) (u_1,v_1) \to (u_2,v_2) (u1,v1)→(u2,v2)。考虑 P P P 关于 l l l 的对称点 P ′ ( x 1 , y 1 ) P'(x_1,y_1) P′(x1,y1) 有 y 0 − x 0 = y 1 − x 1 y_0-x_0=y_1-x_1 y0−x0=y1−x1,且 x 1 + y 1 + x 0 + y 0 = 2 B x_1+y_1+x_0+y_0=2B x1+y1+x0+y0=2B,可得 P ′ ( B − y 0 , B − x 0 ) P'(B-y_0,B-x_0) P′(B−y0,B−x0)。则鱼头 Q Q Q 和 P ′ P' P′ 的中点即为 P P P,因而有 Q ( 2 x 0 + y 0 − B , 2 y 0 + x 0 − B ) Q(2x_0+y_0-B,2y_0+x_0-B) Q(2x0+y0−B,2y0+x0−B) 且满足 Q Q Q 在第一象限。因而 2 x 0 + y 0 ≥ B , 2 y 0 + x 0 ≥ B 2x_0+y_0 \geq B,2y_0+x_0 \geq B 2x0+y0≥B,2y0+x0≥B。此外还有限制 x 0 ∈ [ u 1 , u 2 ] , y 0 ∈ [ v 1 , v 2 ] , x 0 + y 0 ≤ B x_0 \in [u_1,u_2],y_0 \in [v_1,v_2],x_0+y_0 \leq B x0∈[u1,u2],y0∈[v1,v2],x0+y0≤B。
考虑对于以上的限制进行计算合法的格点数。首先仅考虑 x = u 1 , y = v 2 , x + y = B x=u_1,y=v_2,x+y=B x=u1,y=v2,x+y=B 围成的区域,点数为 1 2 ( u 2 − u 1 ) ( u 2 − u 1 − 1 ) \dfrac{1}{2}(u_2-u_1)(u_2-u_1-1) 21(u2−u1)(u2−u1−1)。接下来考虑 2 x + y = B 2x+y=B 2x+y=B 和 x + 2 y = B x+2y=B x+2y=B。
对于 2 x + y = B 2x+y=B 2x+y=B,情况如下:
D ( u 1 , B − 2 u 1 ) D(u_1,B-2u_1) D(u1,B−2u1), E ( B − v 2 2 , v 2 ) E\left(\dfrac{B-v_2}{2},v_2\right) E(2B−v2,v2),因而 Δ A D E \Delta ADE ΔADE 根据 E E E 是否为整点可以得到其覆盖点数为
- E E E 整点: 1 2 ( B − 2 u 1 − v 2 + 2 ) 2 \dfrac{1}{2}(B-2u_1-v_2+2)^2 21(B−2u1−v2+2)2;
- E E E 不是整点: 1 2 ( B − 2 u 1 − v 2 + 1 ) ( B − 2 u 1 − v 2 + 3 ) \dfrac{1}{2}(B-2u_1-v_2+1)(B-2u_1-v_2+3) 21(B−2u1−v2+1)(B−2u1−v2+3)。
同理 x + 2 y = B x+2y=B x+2y=B 的情况为 D ( u 1 , B − u 1 2 ) D\left(u_1,\dfrac{B-u_1}{2}\right) D(u1,2B−u1) , E ( B − 2 v 2 , v 2 ) E(B-2v_2,v_2) E(B−2v2,v2)。此时根据 D D D 是否为整点可以计算出 Δ A D E \Delta ADE ΔADE 覆盖点数为
- D D D 为整点: 1 2 ( B − 2 v 2 − u 2 + 2 ) 2 \dfrac{1}{2}(B-2v_2-u_2+2)^2 21(B−2v2−u2+2)2;
- D D D 不为整点: 1 2 ( B − 2 v 2 − u 2 ) ( B − 2 v 2 − u 2 + 3 ) \dfrac{1}{2}(B-2v_2-u_2)(B-2v_2-u_2+3) 21(B−2v2−u2)(B−2v2−u2+3)。
接下来考虑两条线交叠的下方不规则区域:
将区域拆分为 A F G H AFGH AFGH 和 Δ G H E \Delta GHE ΔGHE。 G G G 坐标为 ( B 3 , B 3 ) \left(\dfrac{B}{3},\dfrac{B}{3}\right) (3B,3B),首先将 G H GH GH 向右平移至横坐标为一整点后,计算出 G H GH GH 的长 B − 2 ⌈ B 3 ⌉ + 1 B-2\left \lceil \dfrac{B}{3}\right \rceil+1 B−2⌈3B⌉+1 后 Δ G H E \Delta GHE ΔGHE 部分和上面问题等同;同时, G H A F GHAF GHAF 范围内的整点数可以由 Δ F A I \Delta FAI ΔFAI 中整点数目减去 Δ G H I \Delta GHI ΔGHI 中个数得到。或者使用 Pick 定理也可。
对于直线上侧的点,无限制,答案为 1 2 ( u 2 − u 1 ) ( u 2 − u 1 − 1 ) \dfrac{1}{2}(u_2-u_1)(u_2-u_1-1) 21(u2−u1)(u2−u1−1)。因而对于一条给定的线段都可以实现 O ( 1 ) \mathcal O(1) O(1) 的计算合法的鱼数目。
插入一条边时,需要考虑 ( u 1 , v 1 ) (u_1,v_1) (u1,v1) 左侧是否有一条线段末端在 ( u 1 , v 1 ) (u_1,v_1) (u1,v1),右侧同理。首先进行延申,去除旧线段然后插入延申后的新线段。对删除,找到目标线段,该目标线段一定是完全覆盖删除段的。这时等效于先把目标线段删除,再插入两端未删除部分。总复杂度为珂朵莉树的 O ( q log q ) \mathcal O(q \log q) O(qlogq)。
#include <bits/stdc++.h>
using namespace std;
long long ans;
const long long inf = 0x3f3f3f3fll;
class Chtholly
{
long long sum;
long long S(long long l, long long r)
{
if (l > r)
return 0;
long long ss = (sum + l + sum + r) * (r - l + 1) / 2;
long long mod2 = 0;
long long K = max(0ll, (r - l + 1) / 2 - 1);
mod2 += K;
l += K * 2;
for (long long i = l; i <= r; i++)
mod2 += (sum + i) % 2;
return (ss - mod2) / 2;
}
long long cal(long long x, long long X)
{
long long res = 0;
long long R = max({x, (X + 1) / 2, X + X - sum});
if (x + x > (sum + x) / 2)
res += S(x, R - 1);
else
{
long long p = sum / 3;
while (p + p < (sum + p) / 2)
p++;
while (p + p - 2 >= (sum + p - 1) / 2)
p--;
p = min(p, R);
res += S(p, R - 1);
if (p > x)
res += (x + x + p - 1 + p - 1) * (p - x) / 2;
}
if (R <= X)
res += X * (X - R + 1);
long long len = X - x;
return res + len * (len + 1) / 2 - (x + X) * (X - x + 1) / 2;
}
set<pair<long long, long long>> t;
public:
void init(long long sum)
{
this->sum = sum;
}
void add(long long l, long long r)
{
auto it = t.lower_bound(make_pair(r, r));
if (it != t.end() && it->first == r)
{
r = it->second;
ans -= cal(it->first, it->second);
t.erase(it);
}
it = t.lower_bound(make_pair(r, r));
if (it != t.begin())
{
it = prev(it);
if (it->second == l)
{
l = it->first;
ans -= cal(it->first, it->second);
t.erase(it);
}
}
t.insert(make_pair(l, r));
ans += cal(l, r);
}
void del(long long l, long long r)
{
auto it = prev(t.upper_bound({l, inf}));
auto old = *it;
ans -= cal(it->first, it->second);
t.erase(it);
if (old.first < l)
add(old.first, l);
if (old.second > r)
add(r, old.second);
}
};
int main()
{
map<long long, Chtholly> t;
int q, op;
long long x1, x2, y1, y2;
scanf("%d", &q);
while(q--)
{
scanf("%d%lld%lld%lld%lld", &op, &x1, &y1, &x2, &y2);
if (x1 > x2)
{
swap(x1, x2);
swap(y1, y2);
}
long long sum = x1 + y1;
if(t.count(sum) == 0)
t[sum].init(sum);
if (op == 1)
t[sum].add(x1, x2);
else
t[sum].del(x1, x2);
printf("%lld\n", ans);
}
return 0;
}
K NIO’s Sword
题意:初始 x = 0 x=0 x=0,给定 n n n,每次执行操作 x ← 10 x + A x \leftarrow 10x+A x←10x+A, A ∈ [ 0 , 9 ] A \in [0,9] A∈[0,9],问使 x m o d n x \bmod n xmodn 从 1 1 1 依次变化到 n n n 需要最少执行几次操作。 n ≤ 1 × 1 0 6 n \leq 1\times 10^6 n≤1×106。
解法:从 i i i 到 i + 1 i+1 i+1 有 1 0 k i + y ≡ i + 1 ( m o d n ) 10^ki+y \equiv i+1 \pmod n 10ki+y≡i+1(modn),其中 y < 1 0 k y <10^k y<10k, k k k 为操作次数。因而 y ≡ i ( 1 − 1 0 k ) + 1 ( m o d n ) y \equiv i(1-10^k)+1 \pmod n y≡i(1−10k)+1(modn)。因而枚举 k k k 即可。注意 n = 1 n=1 n=1 时答案为 0 0 0。
#include <bits/stdc++.h>
using namespace std;
long long th[20];
int main()
{
th[0] = 1;
for (int i = 1; i < 20;i++)
th[i] = th[i - 1] * 10;
int n;
scanf("%d", &n);
long long ans = 0;
for (int i = 0; i < n;i++)
for (int j = 0;;j++)
{
long long x = (1 - (th[j] - 1) * i % n + n) % n;
if (x < th[j])
{
ans += j;
break;
}
}
printf("%lld", ans);
return 0;
}
L Black Hole
题意:给定初始正 n n n 面体和棱长,每次将其面心相连构成新正多面体,进行 k k k 次,问最终是几面体、棱长多少。 n ≤ 200 n \leq 200 n≤200, k ≤ 20 k \leq 20 k≤20, a ≤ 1000 a \leq 1000 a≤1000。
解法:只有五种正多面体:正四、六、八、十二、二十面体。这种变化规律下,四面体还是四面体,正六、八面体互换,正十二、二十面体互换。这是因为正六面体有 8 8 8 个顶点,正八面体有 6 6 6 个顶点,正十二面体有 20 20 20 个顶点,正二十面体有 12 12 12 个顶点。
正四面体内缩之后边长变为 a 3 \dfrac{a}{3} 3a,正六面体内缩后边长变为 a 2 \dfrac{a}{\sqrt 2} 2a,正八面体内缩后边长变为 2 a 3 \dfrac{\sqrt 2a}{3} 32a。
对于正十二面体,每个面为正五边形。考虑面心到棱的距离为 h = a 2 tan 5 4 ∘ h=\dfrac{a}{2} \tan 54^\circ h=2atan54∘,两个面的二面角为 θ = 2 arctan ( ϕ + 1 ) = 2 arcsin 2 5 − 5 \theta=2\arctan(\phi+1)=2\arcsin \sqrt{\dfrac{2}{5-\sqrt 5}} θ=2arctan(ϕ+1)=2arcsin5−52,其中 ϕ = 5 − 1 2 \phi=\dfrac{\sqrt 5-1}{2} ϕ=25−1,则面心距离为新的棱长,为 2 h sin θ 2 = a tan 5 4 ∘ 2 5 − 5 2h \sin \dfrac{\theta}{2}=a \tan 54^\circ \sqrt{\dfrac{2}{5-\sqrt 5}} 2hsin2θ=atan54∘5−52。
对于正二十面体,每个面为等边三角形。面心到棱长距离 h = a 2 3 h=\dfrac{a}{2\sqrt 3} h=23a,两个面二面角为 θ = 2 arctan ( ϕ + 2 ) = 2 arcsin 1 + 5 2 3 \theta=2\arctan(\phi+2)=2\arcsin \dfrac{1+\sqrt 5}{2\sqrt 3} θ=2arctan(ϕ+2)=2arcsin231+5,因而面心距 2 h sin θ 2 = a ( 1 + 5 ) 6 2h\sin \dfrac{\theta}{2}=\dfrac{a(1+\sqrt 5)}{6} 2hsin2θ=6a(1+5)。
#include <bits/stdc++.h>
using namespace std;
const int N = 205;
using db = double;
const db phi = (sqrt(5) - 1) / 2, pi = acos(-1);
int n, k, T[N];
db calc(db a, int t) {
switch (t) {
case 4: return a / 3;
case 6: return a / sqrt(2);
case 8: return a * sqrt(2) / 3;
case 12: return a * tan(54 * pi / 180) * sin(atan(phi + 1));
case 20: return a / sqrt(3) * sin(atan(phi + 2));
}
return -1;
}
void Solve() {
db a;
scanf("%d%lf%d", &n, &a, &k);
if (!T[n]) return puts("impossible"), void();
while (k--) a = calc(a, n), n = T[n];
printf("possible %d %.12lf\n", n, a);
}
int main() {
int t = 1;
T[4] = 4, T[6] = 8, T[8] = 6, T[12] = 20, T[20] = 12;
scanf("%d", &t);
while (t--) Solve();
return 0;
}
M Monotone Chain
题意:给定平面 n n n 个点 ( x i , y i ) (x_i,y_i) (xi,yi),找到两个向量 a , b \textbf{a},\textbf{b} a,b 满足 ∀ 1 ≤ i ≤ j ≤ n \forall 1 \leq i \leq j \leq n ∀1≤i≤j≤n, ( OA i − a ) ⋅ b ≤ ( OA j − a ) ⋅ b (\textbf{OA}_i-\textbf a)\cdot {\textbf{b}} \leq (\textbf{OA}_j-\textbf a)\cdot {\textbf{b}} (OAi−a)⋅b≤(OAj−a)⋅b。 n ≤ 1 × 1 0 5 n \leq 1\times 10^5 n≤1×105,点范围 ∣ x i ∣ , ∣ y i ∣ ≤ 1 × 1 0 5 |x_i|,|y_i| \leq 1\times 10^5 ∣xi∣,∣yi∣≤1×105。
解法:将原式化为 OA i ⋅ b ≤ OA j ⋅ b \textbf{OA}_i\cdot {\textbf{b}} \leq \textbf{OA}_j\cdot {\textbf{b}} OAi⋅b≤OAj⋅b,即是满足 OA i ⋅ b \textbf{OA}_i\cdot {\textbf{b}} OAi⋅b 单调递增。进一步的化简可以得到 A i A j ⋅ b ≤ 0 \textbf{A}_i{\textbf{A}}_j \cdot {\textbf{b}} \leq 0 AiAj⋅b≤0。那么问题转化为每个相邻的 A i A i + 1 A_iA_{i+1} AiAi+1 确定了一个过原点的半个平面,问这些平面有无交点。这里可以无脑使用半平面交,但是更好的做法是使用双指针。首先对所有的不重合的 A i A i + 1 A_iA_{i+1} AiAi+1 的射线方向进行极角排序,然后统计覆盖的射线数目,符合条件即输出当前边界的方向向量。全程可以不使用浮点数,复杂度 O ( n log n + n ) \mathcal O(n \log n+n) O(nlogn+n)。
#include <bits/stdc++.h>
using namespace std;
using _T = long long; // 全局数据类型,可修改为 long long 等
constexpr _T eps = 0;
constexpr long double PI=3.1415926535897932384l;
// 点与向量
template<typename T> struct point
{
T x, y;
bool operator==(const point &a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); }
bool operator<(const point &a) const
{
if (abs(x - a.x) <= eps)
return y < a.y - eps;
return x < a.x - eps;
}
bool operator>(const point &a) const { return !(*this < a || *this == a); }
point operator+(const point &a) const { return {x + a.x, y + a.y}; }
point operator-(const point &a) const { return {x - a.x, y - a.y}; }
point operator-() const { return {-x, -y}; }
point operator*(const T k) const { return {k * x, k * y}; }
point operator/(const T k) const { return {x / k, y / k}; }
T operator*(const point &a) const { return x * a.x + y * a.y; } // 点积
T operator^(const point &a) const { return x * a.y - y * a.x; } // 叉积,注意优先级
int toleft(const point &a) const
{
const auto t = (*this) ^ a;
return (t > eps) - (t < -eps);
} // to-left 测试
T len2() const { return (*this) * (*this); } // 向量长度的平方
T dis2(const point &a) const { return (a - (*this)).len2(); } // 两点距离的平方
// 涉及浮点数
long double len() const { return sqrtl(len2()); } // 向量长度
long double dis(const point &a) const { return sqrtl(dis2(a)); } // 两点距离
long double ang(const point &a) const { return acosl(max(-1.0, min(1.0, ((*this) * a) / (len() * a.len())))); } // 向量夹角
point rot(const long double rad) const { return {x * cos(rad) - y * sin(rad), x * sin(rad) + y * cos(rad)}; } // 逆时针旋转(给定角度)
point rot(const long double cosr, const long double sinr) const { return {x * cosr - y * sinr, x * sinr + y * cosr}; } // 逆时针旋转(给定角度的正弦与余弦)
};
using Point=point<_T>;
// 极角排序
struct argcmp
{
bool operator()(const Point &a,const Point &b) const
{
const auto quad=[](const Point &a)
{
if (a.y<-eps) return 1;
if (a.y>eps) return 4;
if (a.x<-eps) return 5;
if (a.x>eps) return 3;
return 2;
};
const int qa = quad(a), qb = quad(b);
if (qa != qb)
return qa < qb;
const auto t = a ^ b;
// if (abs(t)<=eps) return a*a<b*b-eps; // 不同长度的向量需要分开
return t > eps;
}
};
int main()
{
int n;
scanf("%d", &n);
vector<Point> pt(n);
for (int i = 0; i < n;i++)
scanf("%lld%lld", &pt[i].x, &pt[i].y);
vector<Point> que;
for (int i = 1; i < n;i++)
{
long long x = pt[i].x - pt[i - 1].x, y = pt[i].y - pt[i - 1].y;
if (x || y)
{
Point st = (Point){x, y};
que.emplace_back(st);
}
}
n = que.size();
if (!n)
{
printf("YES\n0 0 1 0");
return 0;
}
sort(que.begin(), que.end(), argcmp());
for (int i = 0; i < n;i++)
que.push_back(que[i]);
for (int l = 0, r = 0; l < n;l++)
{
while (r < l + n && (que[l] ^ que[r]) >= 0)
r++;
if(r == l + n)
{
printf("YES\n0 0 %lld %lld\n", -que[l].y, que[l].x);
return 0;
}
}
printf("NO");
return 0;
}
N Particle Arts
题意:给定序列 { a n } \{a_n\} {an},可以执行无穷多次操作:选定两个数字 a i , a j a_i,a_j ai,aj,然后 a i ← a i & a j a_i \leftarrow a_i \& a_j ai←ai&aj, a j ← a i ∣ a j a_j \leftarrow a_i | a_j aj←ai∣aj,问最大方差。 n ≤ 1 × 1 0 5 n \leq 1\times 10^5 n≤1×105, a i ≤ 2 15 a_i \leq 2^{15} ai≤215。
解法:
a
i
+
a
j
=
(
a
i
&
a
j
)
+
(
a
i
∣
a
j
)
a_i+a_j=(a_i\&a_j)+(a_i|a_j)
ai+aj=(ai&aj)+(ai∣aj),因而
σ
\sigma
σ 不变。当
μ
\mu
μ 最大时一定是数字差距最大的。因而对于每一位分开考虑,若第
i
i
i 位上有
k
k
k 个
1
1
1 则把最大的
k
k
k 个数字加上
2
i
2^i
2i,这样得到新序列
{
b
n
}
\{b_n\}
{bn}。然后根据公式计算
μ
=
1
n
3
∑
i
=
1
n
(
n
b
i
−
∑
j
=
1
n
b
j
)
2
\displaystyle \mu=\dfrac{1}{n^3}\sum_{i=1}^n \left(nb_i-\sum_{j=1}^n b_j\right)^2
μ=n31i=1∑n(nbi−j=1∑nbj)2 即可。注意计算过程中会爆 long long
,需要使用 int_128
。
#include <bits/stdc++.h>
using namespace std;
void print(__int128_t x)
{
string s;
while(x)
{
s += x % 10 + 48;
x /= 10;
}
if(s.empty())
s += "0";
reverse(s.begin(), s.end());
cout << s;
}
int main()
{
int n;
scanf("%d", &n);
vector<long long> a(n);
long long sum = 0;
for (int i = 0; i < n;i++)
{
scanf("%lld", &a[i]);
sum += a[i];
}
vector<int> times(21, 0);
for (auto i : a)
for (int j = 0; j <= 20;j++)
if (i & (1 << j))
times[j]++;
for (auto &i : a)
i = 0;
for (int i = 0; i <= 20;i++)
for (int j = 0; j < times[i];j++)
a[j] |= 1ll << i;
__int128_t ans = 0;
for (int i = 0; i < n;i++)
{
__int128_t now = a[i] * n - sum;
ans += now * now;
}
__int128_t x = ans, y = (__int128_t)n * n * n;
__int128_t g = __gcd(x, y);
print(x / g);
printf("/");
print(y / g);
return 0;
}