2022CCPC黑龙江省赛题解ACEFGHIL
题目链接:
https://codeforces.com/gym/103688
代码链接:
https://hytidel.lanzoub.com/b031d28yh
密码:evfs
I. Equal Sum Arrays
题意
对∀n∈N∗\forall n\in\mathbb{N}^*∀n∈N∗,定义函数f(n)f(n)f(n)为使得元素之和为nnn的正整数序列的个数.如f(3)=4f(3)=4f(3)=4,满足的序列有{1,1,1},{1,2},{2,1},{3}\{1,1,1\},\{1,2\},\{2,1\},\{3\}{1,1,1},{1,2},{2,1},{3}.给定k (1≤k≤20)k\ \ (1\leq k\leq 20)k (1≤k≤20),求f(k)f(k)f(k).
思路
对f(n)f(n)f(n),转化为在nnn个小球排成一行的(n−1)(n-1)(n−1)个空隙中插板,每个空隙可插可不插,显然f(n)=2n−1f(n)=2^{n-1}f(n)=2n−1.
代码 -> 2022CCPC黑龙江省赛-I(思维)
int main() {
int n; cin >> n;
cout << (1 << n - 1);
}
F. 342 and Xiangqi
题意
如上图为中国象棋中一方的象所能到达的位置.
有t (1≤t≤1e5)t\ \ (1\leq t\leq 1\mathrm{e}5)t (1≤t≤1e5)组测试数据.每组测试数据输入四个整数a1,a2,b1,b2 (1≤a1,a2,b1,b2≤7,a1≠a2,b1≠b2)a_1,a_2,b_1,b_2\ \ (1\leq a_1,a_2,b_1,b_2\leq 7,a_1\neq a_2,b_1\neq b_2)a1,a2,b1,b2 (1≤a1,a2,b1,b2≤7,a1=a2,b1=b2),分别表示初始时一方的象的位置和目标位置.求将两只象从初始位置移动到目标位置的最少步数,两只象视为相同.
思路
任一有交叉的路径(即移动过程中一只象可能移动到与另一只象相同的位置)可调整为不交叉的路径.
[证] 因两只象视为相同,则在路径即将发生交叉时,可视为要移动的象A的灵魂传给了在其下一步到达的位置上的象B,
随后象B代替A到达目标位置,此时路径即可不交叉.
路径不交叉,则两只象的移动可视为独立,建图后用Floyd求出任意两个象可到达的位置间的最短路即可.时间复杂度O(73)O(7^3)O(73).
代码 -> 2022CCPC黑龙江省赛-F(思维+Floyd)
const int MAXN = 10;
int n = 7; // 节点数
int dis[MAXN][MAXN]; // dis[u][v]表示节点u到节点v的最短路
void floyd() {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
void init() {
// 初始化
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) dis[i][j] = 0;
else dis[i][j] = INF;
}
}
// 建图
dis[1][2] = dis[1][3] = 1;
dis[2][1] = dis[2][4] = 1;
dis[3][1] = dis[3][4] = 1;
dis[4][2] = dis[4][3] = dis[4][5] = dis[4][6] = 1;
dis[5][4] = dis[5][7] = 1;
dis[6][4] = dis[6][7] = 1;
dis[7][5] = dis[7][6] = 1;
floyd(); // 预处理出任意两节点间的最短路
}
void solve() {
init();
CaseT{
int a1, a2, b1, b2; cin >> a1 >> a2 >> b1 >> b2;
int ans = dis[a1][b1] + dis[a2][b2];
ans = min(ans, dis[a1][b2] + dis[a2][b1]);
cout << ans << endl;
}
}
int main() {
solve();
}
H. Kanbun
题意
给定一个长度为n (3≤n≤1e5)n\ \ (3\leq n\leq 1\mathrm{e}5)n (3≤n≤1e5)的只包含’(‘、’-‘、’)‘且符合括号匹配规则的字符串,下标从111开始.按如下规则阅读该字符串,输出每次阅读的字符的下标:①从第一个字符开始阅读;②对第iii个字符,若它是’-‘或’)‘,则直接阅读,并跳到第(i+1)(i+1)(i+1)个字符;③对第iii个字符,若它是’(‘,先找到与其匹配的’)',假设其下标为jjj,先阅读第(i+1)(i+1)(i+1)到第jjj个字符,再阅读第iii个字符,最后跳到第(j+1)(j+1)(j+1)个字符;④阅读到第(n+1)(n+1)(n+1)个字符时终止.
思路I
实现函数dfs(l,r)表示阅读s[l⋯r]s[l\cdots r]s[l⋯r].从左往右阅读,遇到’-‘或’)‘时直接阅读;遇到’('时先找到与其匹配的右括号,再递归到它们之间的区间即可.注意边界有l>rl>rl>r时的"()“和l=rl=rl=r”(-)"两种情况.
最坏的情况是字符串为5e45\mathrm{e4}5e4个左括号和5e45\mathrm{e}45e4个右括号,若对每个左括号都暴力找到其对应的右括号,时间复杂度1+2+⋯+(n−1)=n(n−1)21+2+\cdots+(n-1)=\dfrac{n(n-1)}{2}1+2+⋯+(n−1)=2n(n−1),会TLE.考虑优化,注意可在DFS前先预处理出每个左括号匹配的右括号,该过程可通过栈实现.总时间复杂度O(n)O(n)O(n).
代码I -> 2022CCPC黑龙江省赛-H(DFS)
const int MAXN = 1e5 + 5;
int n; // 长度
char s[MAXN];
int match[MAXN]; // match[l]=r表示下标为l的左括号匹配下标为r的右括号
void dfs(int l, int r) {
if (l > r) return; // ()的情况
if (l == r) { // (-)的情况
cout << l << ' ';
return;
}
for (int i = l; i <= r; i++) {
if (s[i] == '-' || s[i] == ')') cout << i << ' ';
else {
// 暴力求每个左括号匹配的右括号,TLE
//int sum = 1; // 左括号+1,右括号-1
//int j = i + 1;
//while (sum) {
// if (s[j] == '(') sum++;
// else if (s[j] == ')') {
// sum--;
// if (!sum) break;
// }
// j++;
//}
int j = match[i];
// cout << '(' << i + 1 << ',' << j - 1 << ") "; // 每次递归到的小区间
dfs(i + 1, j - 1);
cout << j << ' ';
cout << i << ' ';
i = j; // 因为还有i++
}
}
}
void solve() {
cin >> n >> s + 1;
// 预处理每个左括号对应的右括号
stack<int> stk;
for (int i = 1; i <= n; i++) {
if (s[i] == '(') stk.push(i); // 左括号入栈
else if (s[i] == ')') { // 找到一组匹配
match[stk.top()] = i;
stk.pop(); // 左括号出栈
}
}
dfs(1, n); // 从整个串开始搜
}
int main() {
solve();
}
思路II
观察样例知:该阅读顺序等价于遇到’-‘直接输出下标;遇到’(‘时待输出与其匹配的’)'的下标后再输出其下标.该过程可用栈实现,时间复杂度O(n)O(n)O(n).
代码II -> 2022CCPC黑龙江省赛-H(思维)
const int MAXN = 1e5 + 5;
int n; // 长度
char s[MAXN];
void solve() {
cin >> n >> s + 1;
// 预处理每个左括号对应的右括号
stack<int> stk;
for (int i = 1; i <= n; i++) {
if (s[i] == '-') cout << i << ' ';
else if (s[i] == '(') stk.push(i); // 左括号入栈
else { // 找到一组匹配
cout << i << ' ' << stk.top() << ' ';
stk.pop(); // 左括号出栈
}
}
}
int main() {
solve();
}
A. Bookshelf Filling
题意
A型书高度为aaa,宽度为111;B型书高度为b (b>a)b\ \ (b>a)b (b>a),宽度为111.有一高度为h (h≥b)h\ \ (h\geq b)h (h≥b)的书架,nnn本A型书和mmm本B型书.现将书按高度非降序依次放在书架上,即B型书都在A型书的右边.现可从最右边选k (0≤k≤m−1)k\ \ (0\leq k\leq m-1)k (0≤k≤m−1)本B型书横着放在竖着放的书的上方.求将所有书放好至少需占用书架的宽度.
有t (1≤t≤1000)t\ \ (1\leq t\leq 1000)t (1≤t≤1000)组测试数据.每组测试数据输入四个整数a,b,n,m,h (1≤a<b≤h≤1e6,1≤n,m≤1e9)a,b,n,m,h\ \ (1\leq a<b\leq h\leq 1\mathrm{e}6,1\leq n,m\leq 1\mathrm{e}9)a,b,n,m,h (1≤a<b≤h≤1e6,1≤n,m≤1e9).
思路
设竖着放的B型书剩下xxx本,则可放剩下的(m−x)(m-x)(m−x)本B型书的区域为上图的紫色和粉色区域,显然它们最多能放的B型书共[(b−a)⋅⌊nb⌋+(h−b)⋅⌊n+xb⌋]\left[(b-a)\cdot\left\lfloor\dfrac{n}{b}\right\rfloor+(h-b)\cdot \left\lfloor\dfrac{n+x}{b}\right\rfloor\right][(b−a)⋅⌊bn⌋+(h−b)⋅⌊bn+x⌋]本,注意此处可能爆int.
因为竖着放的B型书越少,则越可能放不下,即具有二段性.二分xxx,判断能放得下的B型书数是否≥m\geq m≥m即可.注意至多选(m−1)(m-1)(m−1)本B型书,故二分的左边界为111.
代码 -> 2022CCPC黑龙江省赛-A(二分)
const int MAXN = 1e5 + 5;
ll a, b, n, m, h;
bool check(int x) {
return (b - a) * (n / b) + (h - b) * ((n + x) / b) + x >= m;
}
void solve() {
cin >> a >> b >> n >> m >> h;
int l = 1, r = m; // 注意l最小为1
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << n + l << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
L. Let’s Swap
题意
现有一个对字符串sss的操作:先选择sss的一个长度为l (1≤l≤∣s∣)l\ \ (1\leq l\leq |s|)l (1≤l≤∣s∣)的前缀,则sss可表示为ABABAB,其中∣A∣=l,B|A|=l,B∣A∣=l,B可为空串.交换两部分,得到字符串BABABA,再反转整个串,得到(BA)r(BA)^r(BA)r.现对前缀长度的选择有限制,只能选择长度l1l_1l1或l2l_2l2.
有t (1≤t≤5e5)t\ \ (1\leq t\leq 5\mathrm{e}5)t (1≤t≤5e5)组测试数据.每次测试数据第一行输入字符串sss,第二行输入字符串ttt,第三行输入两个整数l1,l2 (1≤l1,l2≤∣s∣=∣t∣,l1≠l2)l_1,l_2\ \ (1\leq l_1,l_2\leq |s|=|t|,l_1\neq l_2)l1,l2 (1≤l1,l2≤∣s∣=∣t∣,l1=l2).数据保证所有测试数据的∣s∣|s|∣s∣之和不超过5e55\mathrm{e}55e5.
对每组测试数据,若能通过若干次(可能为零次)操作将sss转化为ttt,则输出"yes";否则输出"no".
思路
设取长度为l1l_1l1、l2l_2l2的前缀的操作分别为AAA和BBB.注意到连续两个同种操作会相互抵消,则操作方式只能为ABAB⋯ABAB\cdotsABAB⋯或BABA⋯BABA\cdotsBABA⋯,而(AB)−1=BA(AB)^{-1}=BA(AB)−1=BA,则操作方式有(AB)n(AB)^n(AB)n和A(AB)nA(AB)^nA(AB)n两种,其中nnn可为负数.
先对串sss做一次操作AAA得到串tmpstmpstmps.预处理出串sss、ttt、tmpstmpstmps的哈希值ha1[]ha1[]ha1[]、ha2[]ha2[]ha2[]、ha3[]ha3[]ha3[].每次对串sss和tmpstmpstmps做nnn次操作ABABAB,每次考察得到的串的哈希值是否等于ha2[n]ha2[n]ha2[n].
本题至此已可以AC,但事实上还可优化.注意到一次操作ABABAB、BABABA在原串上等价于循环左移、右移∣l1−l2∣|l_1-l_2|∣l1−l2∣个单位,则合法的循环位移长度为k∣l1−l2∣ mod nk|l_1-l_2|\ \mathrm{mod}\ nk∣l1−l2∣ mod n.取d=gcd(∣l1−l2∣,n)d=\gcd(|l_1-l_2|,n)d=gcd(∣l1−l2∣,n),则两种操作每次平移ddd个单位,至多平移nd\dfrac{n}{d}dn次即可.
代码 -> 2022CCPC黑龙江省赛-L(字符串哈希) By : Sakurasss
const int MAXN = 5e5 + 5;
const int P = 13331;
string s, t, tmps; // 原串、目标串、原串做一次A操作得到的串
int l1, l2; // 可选前缀长度
int n; // 长度
ull ha1[MAXN], ha2[MAXN], ha3[MAXN]; // s、t、tmps的哈希值
ull Ppow[MAXN]; // P的乘方
ull get_hash(int op, int l, int r) { // 求s、t、tmps子串[l,r]的哈希
if (op == 1) return ha1[r] - ha1[l - 1] * Ppow[r - l + 1];
else if (op == 2) return ha2[r] - ha2[l - 1] * Ppow[r - l + 1];
else return ha3[r] - ha3[l - 1] * Ppow[r - l + 1];
}
void init() { // // 预处理出三个字符串的哈希值
Ppow[0] = 1;
for (int i = 1; i <= n; i++) {
ha1[i] = ha1[i - 1] * P + s[i];
ha2[i] = ha2[i - 1] * P + t[i];
ha3[i] = ha3[i - 1] * P + tmps[i];
Ppow[i] = Ppow[i - 1] * P;
}
}
void solve() {
cin >> s >> t >> l1 >> l2;
// 对s串做一次A操作
tmps = s;
reverse(tmps.begin(), tmps.begin() + l1), reverse(tmps.begin() + l1, tmps.end());
n = s.length();
s = " " + s, t = " " + t, tmps = " " + tmps; // 下标从1开始
init();
// 枚举做变换AB得到的串
int begin = 0; // 选择的前缀的长度
int d = gcd(abs(l1 - l2), n);
int t = n;
while (true) {
ull tmp1 = ha1[n - begin] + get_hash(1, n - begin + 1, n) * Ppow[n - begin];
ull tmp3 = ha3[n - begin] + get_hash(3, n - begin + 1, n) * Ppow[n - begin];
if (tmp1 == ha2[n] || tmp3 == ha2[n]) {
cout << "yes" << endl;
return;
}
begin += d;
if (begin > n) break;
}
cout << "no" << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
G. Chevonne’s Necklace
题意
nnn颗珍珠串成一串项链,珍珠顺时针编号1∼n1\sim n1∼n.每颗珍珠上有一个非负整数cic_ici,每次操作可选择一颗满足ci≥1c_i\geq 1ci≥1的珍珠iii,移除从它顺时针开始数的cic_ici颗珍珠.若剩下的珍珠数不足cic_ici,则不能选择珍珠iii.操作后剩下的珍珠重新串成项链.求最多能移除多少颗珍珠及其方案数.用每次操作选择的珍珠的编号构成的集合来描述一个方案,两个方案不同当且仅当它们对应的集合不同.
第一行输入整数n (1≤n≤2000)n\ \ (1\leq n\leq 2000)n (1≤n≤2000).第二行输入nnn个整数c1,⋯ ,cn (0≤ci≤2000)c_1,\cdots,c_n\ \ (0\leq c_i\leq 2000)c1,⋯,cn (0≤ci≤2000).
输出两个整数,第一个表示最多能移除多少颗珍珠,第二个表示其方案数,答案对998244353998244353998244353取模.
思路
将珍珠视为物品,以nnn为背包容量、cic_ici为体积、000为价值做背包,则方案数即背包的方案数.
[析] 可疑点:删除一个选中的珍珠时顺带将下一步要选择的珍珠也移除了,导致无法构造出背包的解.
[证] 只需对背包选出的方案构造出原题的一组解.设背包选中的珍珠下标为p1,⋯ ,pkp_1,\cdots,p_kp1,⋯,pk,则∑i=1kcpi≤n\displaystyle \sum_{i=1}^k c_{p_i}\leq ni=1∑kcpi≤n.
dis(pi)dis(p_i)dis(pi)表示珍珠pip_ipi与顺时针方向的下一个选中的珍珠(即珍珠p(i mod n)+1p_{(i\ \mathrm{mod}\ n)+1}p(i mod n)+1)间的距离,
则只有cpi≤dis(pi)c_{p_i}\leq dis(p_i)cpi≤dis(pi)时才能删去珍珠pip_ipi.
下证每次操作时至少∃\exists∃一个珍珠 s.t. cpi≤dis(pi)\ s.t.\ c_{p_i}\leq dis(p_i) s.t. cpi≤dis(pi).若不然,设所有珍珠都满足cpi>dis(pi)c_{p_i}>dis(p_i)cpi>dis(pi),
则∑i=1kcpi>∑i=1kdis(pi)=n\displaystyle\sum_{i=1}^k c_{p_i}>\sum_{i=1}^k dis(p_i)=ni=1∑kcpi>i=1∑kdis(pi)=n,矛盾.
故每次操作时都选中一个满足cpi≤dis(pi)c_{p_i}\leq dis(p_i)cpi≤dis(pi)的珍珠即可,而方案用集合描述,故移除顺序不影响方案数.
显然最终选中的珍珠是从珍珠111开始的连续编号.
代码 -> 2022CCPC黑龙江省赛-G(背包)
const int MAXN = 2005;
const int MOD = 998244353;
int n;
int c[MAXN];
bool removed[MAXN]; // 记录每颗珍珠是否被移除
int dp[MAXN]; // dp[i]表示移除i颗珍珠的方案数
void solve() {
cin >> n;
for (int i = 0; i < n; i++) cin >> c[i];
removed[0] = true;
dp[0] = 1;
for (int i = 0; i < n; i++) { // 枚举物品
if (c[i]) { // c[i]≥1才能选中
for (int j = n; j >= c[i]; j--) { // 枚举体积
removed[j] |= removed[j - c[i]]; // 更新珍珠状态
dp[j] = ((ll)dp[j] + dp[j - c[i]]) % MOD;
}
}
}
int ans = n;
while (!removed[ans]) ans--;
cout << ans << ' ' << dp[ans];
}
int main() {
solve();
}
C. Tree Division
题意
给定一棵包含编号1∼n1\sim n1∼n的nnn个节点的树,其中第i (1≤i≤n)i\ \ (1\leq i\leq n)i (1≤i≤n)个节点有权值aia_iai.判断节点111是否有效,若有效则输出"YES",否则输出"NO".定义节点ttt有效,如果可将nnn个节点划分为两个集合AAA、BBB,使得:①对∀u,v∈A (u≠v)\forall u,v\in A\ \ (u\neq v)∀u,v∈A (u=v),若uuu是从节点ttt到节点vvv的路径,则au<ava_u<a_vau<av;②对∀u,v∈B (u≠v)\forall u,v\in B\ \ (u\neq v)∀u,v∈B (u=v),若uuu是从节点ttt到节点vvv的路径,则au>ava_u>a_vau>av.
第一行输入一个整数n (1≤n≤1e5)n\ \ (1\leq n\leq 1\mathrm{e}5)n (1≤n≤1e5).第二行输入nnn个整数ai (1≤ai≤n)a_i\ \ (1\leq a_i\leq n)ai (1≤ai≤n).接下来(n−1)(n-1)(n−1)行每行输入两个整数x,y (1≤x,y≤n)x,y\ \ (1\leq x,y\leq n)x,y (1≤x,y≤n),表示节点xxx与节点yyy间存在边.
思路 By : watyrtle
注意到以节点111为根节点时,节点111有效的条件为:①对∀u,v∈A (u≠v)\forall u,v\in A\ \ (u\neq v)∀u,v∈A (u=v),若节点vvv是以节点uuu为根节点的子树中的节点,则au<ava_u<a_vau<av,递推得ava_vav是根节点到节点vvv的路径上权值最大的点,且之后放入集合AAA中的节点的权值>av>a_v>av;②对∀u,v∈B (u≠v)\forall u,v\in B\ \ (u\neq v)∀u,v∈B (u=v),若节点vvv是以节点uuu为根节点的子树中的节点,则au>ava_u>a_vau>av,递推得ava_vav是根节点到节点vvv的路径上权值最小的点,且之后放入集合BBB中的节点的权值<av<a_v<av.
贪心策略:将节点放入集合AAA时,保证集合BBB中的节点的最小值尽量大;将节点放入集合BBB中时,保证集合AAA中的节点的最大值尽量小.dp[u][0]dp[u][0]dp[u][0]表示将节点uuu放入集合AAA中时集合BBB中的节点权值的最小值的最大值,dp[u][1]dp[u][1]dp[u][1]表示将节点uuu放入集合BBB中时集合AAA中的节点权值的最大值的最小值.初始条件dp[1][0]=−INF,dp[1][1]=INFdp[1][0]=-INF,dp[1][1]=INFdp[1][0]=−INF,dp[1][1]=INF,都表示不合法的状态.
设节点uuu的一个子节点为vvv.对dp[u][0]dp[u][0]dp[u][0],若vvv能放入集合AAA,由两种情况:①ava_vav严格大于根节点到节点vvv的路径上权值最大的节点(节点uuu),即av>aua_v>a_uav>au;②将vvv放入集合BBB中时集合AAA中的节点权值的最大值的最小值(即dp[v][1]dp[v][1]dp[v][1])严格大于aua_uau,即au<dp[v][1]a_u<dp[v][1]au<dp[v][1].同理可得dp[u][1]dp[u][1]dp[u][1]的状态转移.
状态转移方程:{dp[u][0]=maxedge<u,v>{min{au<av ? dp[v][0] : INF,au<dp[v][1] ? av : INF}}dp[u][1]=minedge<u,v>{max{au>av ? dp[v][1] : −INF,au>dp[v][0] ? av : −INF}}\begin{cases}\displaystyle dp[u][0]=\max_{edge<u,v>}\{\min\{a_u<a_v\ ?\ dp[v][0]\ :\ INF,a_u<dp[v][1]\ ?\ a_v\ :\ INF\}\} \\ \displaystyle dp[u][1]=\min_{edge<u,v>}\{\max\{a_u>a_v\ ?\ dp[v][1]\ :\ -INF,a_u>dp[v][0]\ ?\ a_v\ :\ -INF\}\}\end{cases}⎩⎨⎧dp[u][0]=edge<u,v>max{min{au<av ? dp[v][0] : INF,au<dp[v][1] ? av : INF}}dp[u][1]=edge<u,v>min{max{au>av ? dp[v][1] : −INF,au>dp[v][0] ? av : −INF}}.
若更新后dp[1][0]=INFdp[1][0]=INFdp[1][0]=INF且dp[1][1]=−INFdp[1][1]=-INFdp[1][1]=−INF,则节点111无效,否则有效.
代码 -> 2022CCPC黑龙江省赛-C(贪心+树形DP)
const int MAXN = 1e5 + 5;
int n;
int a[MAXN];
vi edges[MAXN];
vi son[MAXN]; // son[u]表示节点u的儿子节点
// dp[u][0]表示将节点u放入集合A中时集合B中的节点权值的最小值的最大值
int dp[MAXN][2]; // dp[u][1]表示将节点u放入集合B中时集合A中的节点权值的最大值的最小值
void dfs1(int u, int fa) { // 预处理出以节点1为根节点时每个节点的儿子节点:当前节点、前驱节点
for (auto v : edges[u]) {
if (v == fa) continue;
son[u].push_back(v);
dfs1(v, u);
}
}
void dfs2(int u) { // 树形DP
for (auto v : son[u]) {
dfs2(v); // 递归求以v为根节点的子树的信息
dp[u][0] = max(dp[u][0], min(a[u] < a[v] ? dp[v][0] : INF, a[u] < dp[v][1] ? a[v] : INF));
dp[u][1] = min(dp[u][1], max(a[u] > a[v] ? dp[v][1] : -INF, a[u] > dp[v][0] ? a[v] : -INF));
}
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
dp[i][0] = -INF, dp[i][1] = INF; // 初始化为不合法状态
}
for (int i = 0; i < n - 1; i++) {
int x, y; cin >> x >> y;
edges[x].push_back(y), edges[y].push_back(x);
}
dfs1(1, -1);
dfs2(1);
if (dp[1][0] == INF && dp[1][1] == -INF) cout << "NO";
else cout << "YES";
}
int main() {
solve();
}
E. Exclusive Multiplication
题意
对正整数n=∏i=1mpiαi\displaystyle n=\prod_{i=1}^m p_i^{\alpha_i}n=i=1∏mpiαi,定义函数f(n)f(n)f(n)为nnn的奇数次素因子之积,即f(n)=∏i=1mpiαi%2\displaystyle f(n)=\prod_{i=1}^m p_i^{\alpha_i\%2}f(n)=i=1∏mpiαi%2.若nnn无奇数次素因子,定义f(n)=1f(n)=1f(n)=1.
给定n (1≤n≤2e5)n\ \ (1\leq n\leq 2\mathrm{e}5)n (1≤n≤2e5)和nnn个整数b1,⋯ ,bn (1≤bi≤2e5)b_1,\cdots,b_n\ \ (1\leq b_i\leq 2\mathrm{e}5)b1,⋯,bn (1≤bi≤2e5),求∑1≤i<j≤nf(bi×bj)\displaystyle\sum_{1\leq i<j\leq n}f(b_i\times b_j)1≤i<j≤n∑f(bi×bj),答案对1e9+71\mathrm{e}9+71e9+7取模.
思路
考察f(n)f(n)f(n)是否是积性函数.
对a,b∈N∗a,b\in\mathbb{N}^*a,b∈N∗,设a=∏i=1k1piαi,b=∏i=1k2piβi\displaystyle a=\prod_{i=1}^{k_1}p_i^{\alpha_i},b=\prod_{i=1}^{k_2}p_i^{\beta_i}a=i=1∏k1piαi,b=i=1∏k2piβi.
因aaa和bbb中的偶数次素因子对f(a)f(a)f(a)和f(b)f(b)f(b)无贡献,不妨将偶数次素因子除掉,只留下奇数次素因子.
设此时a′=p1α1⋯pnαnq1αn+1⋯qm1αn+m1,b′=p1β1⋯pnβnr1βn+1⋯rm2βn+m2a'=p_1^{\alpha_1}\cdots p_{n}^{\alpha_{n}}q_1^{\alpha_{n+1}}\cdots q_{m_1}^{\alpha_{n+m_1}},b'=p_1^{\beta_1}\cdots p_n^{\beta_n}r_1^{\beta_{n+1}}\cdots r_{m_2}^{\beta_{n+m_2}}a′=p1α1⋯pnαnq1αn+1⋯qm1αn+m1,b′=p1β1⋯pnβnr1βn+1⋯rm2βn+m2,
则f(a)=∏i=1npiαi⋅∏i=1m1qiαn+i,f(b)=∏i=1npiβi⋅∏i=1m2riβn+i\displaystyle f(a)=\prod_{i=1}^{n}p_i^{\alpha_i}\cdot \prod_{i=1}^{m_1}q_i^{\alpha_{n+i}},f(b)=\prod_{i=1}^{n}p_i^{\beta_i}\cdot \prod_{i=1}^{m_2}r_i^{\beta_{n+i}}f(a)=i=1∏npiαi⋅i=1∏m1qiαn+i,f(b)=i=1∏npiβi⋅i=1∏m2riβn+i.
考察ababab中的偶数次素因子的来源:
①对aaa与bbb相同的素因子,ababab的偶数次可能来自aaa的偶数次+b+b+b的偶数次或aaa的奇数次+b+b+b的奇数次,
其中前者在f(a)f(a)f(a)和f(b)f(b)f(b)中都不考虑,后者在f(a)f(a)f(a)和f(b)f(b)f(b)中都考虑.
②对aaa与bbb不同的素因子,ababab中的偶数次只能来自aaa的偶数次+b+b+b的偶数次,两者在f(a)f(a)f(a)和f(b)f(b)f(b)中都不考虑.
综上f(ab)=f(a)f(b)[gcd(f(a),f(b))]2f(ab)=\dfrac{f(a)f(b)}{[\gcd(f(a),f(b))]^2}f(ab)=[gcd(f(a),f(b))]2f(a)f(b),虽f(n)f(n)f(n)不是积性函数,但f(ab)f(ab)f(ab)可由f(a)f(a)f(a)和f(b)f(b)f(b)表示,可先求出f(n)f(n)f(n).
因计算过程与bib_ibi本身无关,只与f(bi)f(b_i)f(bi)有关,可读入时将bib_ibi变为f(bi)f(b_i)f(bi),记其中最大的f(bi)f(b_i)f(bi)为maxbmaxbmaxb.
为计算ans=∑1≤i<j≤nf(bi×bj)\displaystyle ans=\sum_{1\leq i<j\leq n}f(b_i\times b_j)ans=1≤i<j≤n∑f(bi×bj),先计算∑d∣ng(d)d2\displaystyle\sum_{d\mid n}\dfrac{g(d)}{d^2}d∣n∑d2g(d),其中g(n)=∑n=gcd(bi,bj)f(bi)f(bj)\displaystyle g(n)=\sum_{n=\gcd(b_i,b_j)}f(b_i)f(b_j)g(n)=n=gcd(bi,bj)∑f(bi)f(bj).
ansansans中的项:
f(b1b2)f(b_1b_2)f(b1b2) | f(b1b3)f(b_1b_3)f(b1b3) | f(b1b4)f(b_1b_4)f(b1b4) | f(b1b5)f(b_1b_5)f(b1b5) | f(b1b6)f(b_1b_6)f(b1b6) | ⋯\cdots⋯ |
---|---|---|---|---|---|
f(b2b3)f(b_2b_3)f(b2b3) | f(b2b4)f(b_2b_4)f(b2b4) | f(b2b5)f(b_2b_5)f(b2b5) | f(b2b6)f(b_2b_6)f(b2b6) | ⋯\cdots⋯ | |
f(b3b4)f(b_3b_4)f(b3b4) | f(b3b5)f(b_3b_5)f(b3b5) | f(b3b6)f(b_3b_6)f(b3b6) | ⋯\cdots⋯ | ||
f(b4b5)f(b_4b_5)f(b4b5) | f(b4b6)f(b_4b_6)f(b4b6) | ⋯\cdots⋯ | |||
⋮\vdots⋮ | ⋱\ddots⋱ |
∑d∣ng(d)d2\displaystyle\sum_{d\mid n}\dfrac{g(d)}{d^2}d∣n∑d2g(d)中f(n)f(n)f(n)的项:
f(b1b1)f(b_1b_1)f(b1b1) | f(b1b2)f(b_1b_2)f(b1b2) | f(b1b3)f(b_1b_3)f(b1b3) | f(b1b4)f(b_1b_4)f(b1b4) | f(b1b5)f(b_1b_5)f(b1b5) | ⋯\cdots⋯ |
---|---|---|---|---|---|
f(b2b1)f(b_2b_1)f(b2b1) | f(b2b2)f(b_2b_2)f(b2b2) | f(b2b3)f(b_2b_3)f(b2b3) | f(b2b4)f(b_2b_4)f(b2b4) | f(b2b5)f(b_2b_5)f(b2b5) | ⋯\cdots⋯ |
f(b3b1)f(b_3b_1)f(b3b1) | f(b3b2)f(b_3b_2)f(b3b2) | f(b3b3)f(b_3b_3)f(b3b3) | f(b3b4)f(b_3b_4)f(b3b4) | f(b3b5)f(b_3b_5)f(b3b5) | ⋯\cdots⋯ |
f(b4b1)f(b_4b_1)f(b4b1) | f(b4b2)f(b_4b_2)f(b4b2) | f(b4b3)f(b_4b_3)f(b4b3) | f(b4b4)f(b_4b_4)f(b4b4) | f(b4b5)f(b_4b_5)f(b4b5) | ⋯\cdots⋯ |
⋮\vdots⋮ | ⋮\vdots⋮ | ⋮\vdots⋮ | ⋮\vdots⋮ | ⋮\vdots⋮ | ⋱\ddots⋱ |
对比知∑d∣ng(d)d2\displaystyle\sum_{d\mid n}\dfrac{g(d)}{d^2}d∣n∑d2g(d)比ansansans多了下三角形的部分和对角线,而f(i,i)f(i,i)f(i,i)无奇数次素因子,故f(i,i)=1f(i,i)=1f(i,i)=1,进而对角线元素之和为nnn.
故ans=12(∑d∣ng(d)d2−n)\displaystyle ans=\dfrac{1}{2}\left(\sum_{d\mid n}\dfrac{g(d)}{d^2}-n\right)ans=21⎝⎛d∣n∑d2g(d)−n⎠⎞.
考虑如何计算g(n)g(n)g(n).令G(n)=∑n∣dg(d)=∑n∣gcd(bi,bj)f(bi,bj)=[∑n∣bif(bi)]2\displaystyle G(n)=\sum_{n\mid d}g(d)=\sum_{n\mid \gcd(b_i,b_j)}f(b_i,b_j)=\left[\sum_{n|b_i}f(b_i)\right]^2G(n)=n∣d∑g(d)=n∣gcd(bi,bj)∑f(bi,bj)=⎣⎡n∣bi∑f(bi)⎦⎤2.
设fcnt[i]fcnt[i]fcnt[i]表示 s.t. f(bj)=i\ s.t.\ f(b_j)=i s.t. f(bj)=i的bjb_jbj的个数,则G(n)G(n)G(n)可通过fcnt[i]fcnt[i]fcnt[i]求出.
由Mobius反演:g(d)=∑d∣nμ(nd)G(n)\displaystyle g(d)=\sum_{d\mid n}\mu\left(\dfrac{n}{d}\right)G(n)g(d)=d∣n∑μ(dn)G(n).
代码 -> 2022CCPC黑龙江省赛-E(莫反)
const int MAXN = 2e5 + 5;
const int MOD = 1e9 + 7;
int n;
int fb[MAXN]; // fb[i]=f(b_i)
int fcnt[MAXN]; // cnt[i]表示 s.t. f(b_j)=i的b_j的个数
int primes[MAXN], cnt = 0;
bool vis[MAXN];
int mu[MAXN];
int G[MAXN]; // G(n)=Σ_{n|d} g(d)
int g[MAXN]; // g(n)=Σ_{n=gcd(b_i,b_j)} f(b_i)×f(b_j)
void init() { // 预处理mu[]
mu[1] = 1;
for (int i = 2; i < MAXN; i++) {
if (!vis[i]) primes[cnt++] = i, mu[i] = -1;
for (int j = 0; primes[j] * i < MAXN; j++) {
vis[primes[j] * i] = true;
if (i % primes[j] == 0) break;
mu[primes[j] * i] = -mu[i];
}
}
for (int i = 1; i < MAXN; i++) mu[i] = (mu[i] + MOD) % MOD;
}
int inv(int x) { return qpow(x, MOD - 2, MOD); }
void solve() {
init();
cin >> n;
for (int i = 1; i <= n; i++) fb[i] = 1; // 初始化
int maxnum = 0;
for (int i = 1; i <= n; i++) {
int b; cin >> b;
for (int d = 2; d <= b / d; d++) { // 枚举b的约数d
if (b % d == 0) {
int s = 0; // 素因子的次数
while (b % d == 0) b /= d, s++;
if (s & 1) fb[i] = (ll)fb[i] * d % MOD; // s为奇数时才对f(b)有贡献
}
}
if (b > 1) fb[i] = (ll)fb[i] * b % MOD; // 大于sqrt(b)的素因子
fcnt[fb[i]]++;
maxnum = max(maxnum, fb[i]);
}
for (int d = 1; d <= maxnum; d++) { // 枚举约数d
for (int j = d; j <= maxnum; j += d) // 枚举d的倍数
if (fcnt[j]) G[d] = ((ll)G[d] + (ll)fcnt[j] * j % MOD) % MOD;
G[d] = (ll)G[d] * G[d] % MOD;
}
for (int d = 1; d <= maxnum; d++) { // 枚举约数d
for (int j = d; j <= maxnum; j += d) // 枚举d的倍数
g[d] = ((ll)g[d] + (ll)mu[j / d] * G[j] % MOD) % MOD; // 反演出g[]
}
int ans = 0;
for (int d = 1; d <= maxnum; d++)
ans = ((ll)ans + (ll)g[d] * inv(d) % MOD * inv(d) % MOD) % MOD;
ans = (ll)((ans - n) % MOD + MOD) % MOD * inv(2) % MOD;
cout << ans;
}
int main() {
solve();
}