Good Bye 2022: 2023 is NEAR
Problem A. Koxia and Whiteboards
Statement:
Kiyora 有 n n n块白板,从 1 1 1到 n n n编号。初始情况下, 第 i i i块白板上写着一个整数 a i a_i ai 。 Koxia 要进行 m m m次操作。第 j j j次操作是,选择一块白板并将上面的整数修改为 b j b_j bj。 计算全部 m m m次操作后所有白板上数字之和的最大可能值。
solution:
用个优先队列随便搞搞就好。
Problem B. Koxia and Permutation
Statement:
Reve 有两个整数
n
n
n和
k
k
k。 对于一个长度为
n
n
n的排列
p
p
p,我们令
c
c
c是一个长度为
n
−
k
+
1
n - k + 1
n−k+1的数组,其中
c
i
=
m
a
x
(
p
i
,
.
.
.
p
i
+
k
−
1
)
+
m
i
n
(
p
i
,
.
.
.
,
p
i
+
k
−
1
)
c_i = max(p_i,...p_{i + k - 1}) + min(p_i,...,p_{i + k - 1})
ci=max(pi,...pi+k−1)+min(pi,...,pi+k−1)
定义一个排列
p
p
p的代价是
c
c
c中的最大值。 Koxia 希望你构造一个排列使得其代价尽可能小。
solution:
一种可行的构造是: [ n , n − 1 , n − 2 , . . . n − k + 2 , 1 , n − k + 1 , n − k , . . . n − 2 k + 3 , 2... ] [n,n - 1,n - 2,...n - k + 2,1,n - k + 1,n - k,...n - 2k + 3,2...] [n,n−1,n−2,...n−k+2,1,n−k+1,n−k,...n−2k+3,2...]
另一种可行的构造是: [ n , 1 , n − 1 , 2 , n − 2 , 3 , n − 3 , 4 , . . . ] [n,1,n - 1,2,n - 2,3,n-3,4,...] [n,1,n−1,2,n−2,3,n−3,4,...]
Problem C. Koxia and Number Theory
Statement:
Joi 有一个数组 a ( 1 ≤ a i ≤ 1 0 18 ) a(1\leq a_i \leq10^{18}) a(1≤ai≤1018),包含 n ( n ≤ 100 ) n(n\leq 100) n(n≤100)个正整数。Koxia 希望你判定是否存在一个正整数 x x x使得 g c d ( a i + x , a j + x ) = 1 gcd(a_i + x,a_j + x) = 1 gcd(ai+x,aj+x)=1对于所有 1 ≤ i < j ≤ n 1\leq i < j\leq n 1≤i<j≤n都成立。输出yes或no。
Solution:
如果数组中存在两个数 a i , a j a_i,a_j ai,aj相同,那么 x x x必不可能存在,因为 g c d ( a i + x , a j + x ) = a i + x gcd(a_i + x,a_j + x) = a_i + x gcd(ai+x,aj+x)=ai+x。
考虑还有那些情况使得 x x x必不可能存在。
思考一个问题,什么情况下一定存在 i , j i,j i,j使得 g c d ( a i + x , a j + x ) = 2 gcd(a_i + x,a_j + x) = 2 gcd(ai+x,aj+x)=2。答案是,如果同时存在 2 2 2个或以上奇数及 2 2 2个或以上偶数,则无论 x x x是多少,都存在存在 i , j i,j i,j使得 g c d ( a i + x , a j + x ) = 2 gcd(a_i + x,a_j + x) = 2 gcd(ai+x,aj+x)=2。
进一步,是否存在一种情况使得一定存在 i , j i,j i,j使得 g c d ( a i + x , a j + x ) = 3 gcd(a_i + x,a_j + x) = 3 gcd(ai+x,aj+x)=3。可以发现,当至少存在 2 2 2个数满足 a i % 3 = = 0 a_i \% 3 == 0 ai%3==0、至少存在 2 2 2个数满足 a i % 3 = = 1 a_i \% 3 == 1 ai%3==1,至少存在 2 2 2个数满足 a i % 3 = = 2 a_i \% 3 == 2 ai%3==2时,无论 x x x为多少,都存在 i , j i,j i,j使得 g c d ( a i + x , a j + x ) = 2 gcd(a_i + x,a_j + x) = 2 gcd(ai+x,aj+x)=2。
由此类推,要使得 x x x存在,则任意一个模数系都不能被填满 2 2 2次,即
对于一个质数 p p p,令 c n t j cnt_j cntj是 j j j在 [ a 1 m o d p , a 2 m o d p , a 3 m o d p . . . a n m o d p ] [a_1\ mod\ p,a_2\ mod\ p,a_3\ mod\ p...a_n\ mod\ p] [a1 mod p,a2 mod p,a3 mod p...an mod p]中 j j j的出现次数。若 m i n ( c n t 0 , c n t 1 , . . . , c n t p − 1 ) > 1 min(cnt_0,cnt_1,...,cnt_{p - 1}) > 1 min(cnt0,cnt1,...,cntp−1)>1,则必定不存在 x x x满足题意。
实际上 c n t i ≥ 2 cnt_i \geq2 cnti≥2意味着 x ≢ ( m o d p − i ) x\not\equiv\pmod {p - i} x≡(modp−i)
若满足上述条件,则对每一个模数,我们都可以至少列出一条同余式,组成同余方程组,并用中国剩余定理求解出 x x x(当然本题并不需要求解)。
实现时,我们只需枚举 n / 2 n/2 n/2以内的质数,判断该模数系是否符合条件。大于 n / 2 n/2 n/2的质数的模数系不可能被填满两次,故可以不考虑。
代码
#include<bits/stdc++.h>
using namespace std;
int solve()
{
int n;
bool bz = 0;
scanf("%d",&n);
set<long long> S;
vector<vector<int>> used(n,vector<int>(n,0));
vector<long long >ar;
for(int i = 0;i < n;i ++)
{
long long x;
scanf("%lld",&x);
if(S.count(x) != 0)
bz = 1;
ar.push_back(x);
S.insert(x);
}
if(bz)
return 0;
for(int i = 2;i <= n / 2;i ++)
{
for(auto v:ar)
used[i][v % i] += 1;
int cnt = 0;
for(int j = 0;j < i;j ++)
if(used[i][j] >= 2)
cnt ++;
if(cnt == i)
return 0;
}
return 1;
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
if(solve())
printf("Yes\n");
else
printf("No\n");
}
}
D. Koxia and Game
Statement:
Koxia 和 Mahiru 正在用三个长度为 n n n的数组 a , b , c a,b,c a,b,c玩一个游戏。其中 a , b , c a,b,c a,b,c中的每个元素 都是 1 1 1到 n ( n ≤ 1 0 5 ) n(n\leq 10^5) n(n≤105)之间的整数。
游戏持续 n n n轮。在第 i i i轮中,她们进行以下操作:
- 令 S S S是 { a i , b i , c i } \{a_i,b_i,c_i\} {ai,bi,ci}的可重集。
- Koxia 从可重集 S S S中选择一个元素并移除。
- Mahiru 从可重集 S S S的剩余两个元素中选择一个元素。
- 例如第 k k k轮游戏,已知 a k = 3 , b k = 5 , c k = 3 a_k = 3,b_k = 5,c_k = 3 ak=3,bk=5,ck=3,则Koxia从 { 3 , 5 , 3 } \{3,5,3\} {3,5,3}中选择一个元素删除,Mahiru选择剩下一个元素作为 d k d_k dk
记 d i d_i di是第 i i i轮中 Mahiru 选择的元素。如果 d d d是一个排列 ,则 Koxia 获胜;否则 Mahiru 获胜。 现在,只有 a a a和 b b b两个数组是已经确定的。作为 Koxia 的支持者,你希望选择合适的数组 c c c使得 Koxia 获胜。计算这样的 c c c的数量,输出其对 998244353 998244353 998244353取模后的结果。
Koxia 和 Mahiru 会以最优方式行动。
Solution:
意思就是,给你两个大小为 n n n的数组 a , b a,b a,b,让你构造一个数组 c c c,使得Koxia必胜,求必胜的数组 c c c的个数。
首先我们要确保对于任意 { a i , b i , c i } \{a_i,b_i,c_i\} {ai,bi,ci},我们删除一个元素之后,剩下两个元素必须相同,否则无论如何,Mahiru总能使自己不去取到一个排列。
那么有以下两种情况
- 对 a i = = b i a_i == b_i ai==bi, c i c_i ci可以是 1 1 1到 n n n的任意数。删除时,我们选择删除 c i c_i ci
- 对 a i ≠ b i a_i \neq b_i ai=bi, c i c_i ci等于 a i 、 b i a_i、b_i ai、bi其中一个数,删除时,我们删除那个不一样的数。
这样我们可以保证Mahiru每次取数时,都只能取一个数,现在我们要使他取的这些数构成一个排列。
考虑建图。
设 n n n个点,对 ( a i , b i ) (a_i,b_i) (ai,bi)连边,令 a i a_i ai指向 b i b_i bi表示选择 b i b_i bi,令 b i b_i bi指向 a i a_i ai表示选择 a i a_i ai。共有 n n n条边。 d d d可能是一个排列,当且仅当存在一种方式给每条边指定方向使得每个结点都被恰好一条边所指向。不难看出,对于每个连通分量,该连通分量的大小与边的个数相同,否则必然不满足条件。
考虑计数,对每个满足 ∣ V ∣ = ∣ E ∣ |V| = |E| ∣V∣=∣E∣的连通分量,可以视作一棵树加上一条边,有以下两种情况。
- 该联通分量时一棵树加上一个自环,对应上述情况1,有 n n n种方案,其他边只能指向远离自环的方向。
- 该联通分量时一棵基环树。此时,边的定向有两种方案,分别是基环顺时针指向和基环逆时针指向,其他边指向远离环的方向。
最终答案为 n c n t 1 ⋅ 2 c n t 2 n^{cnt_1}\cdot 2^{cnt2} ncnt1⋅2cnt2, c n t 1 cnt_1 cnt1为存在自环的连通分量的个数, c n t 2 cnt_2 cnt2为基环树连通分量的个数。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 100000 + 200
int cnt,poi;
bool vis[N],cir;
vector<int>e[N];
const long long mod = 998244353;
void dfs(int x,int fa)
{
if(vis[x])
return;
poi ++;
vis[x] = 1;
for(auto v:e[x])
{
cnt ++;
dfs(v,x);
if(v == x)
cir = 1;
}
}
void solve()
{
int n;
scanf("%d",&n);
vector<int>a(n);
vector<int>b(n);
for(int i = 1;i <= n;i ++)
{
vis[i] = 0;
e[i].clear();
}
for(int i = 0;i < n;i ++)
scanf("%d",&a[i]);
for(int i = 0;i < n;i ++)
scanf("%d",&b[i]);
for(int i = 0;i < n;i ++)
{
e[a[i]].push_back(b[i]);
e[b[i]].push_back(a[i]);
}
long long ans = 1;
for(int i = 1;i <= n;i ++)
{
if(vis[i])
continue;
cnt = 0,poi = 0,cir = 0;
dfs(i,0);
if(cnt != 2 * poi)
{
ans = 0;
break;
}
else
{
if(cir)
ans = ans * n % mod;
else
ans = ans * 2 % mod;
}
}
printf("%lld\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
solve();
}
E. Koxia and Tree
Statement:
Imi 有一棵由 n n n个结点组成的无向树,其中边从 1 1 1到 n n n编号,第 i i i条边连接了结点 u i u_i ui和 v i v_i vi。另外,树上有 k k k只蝴蝶。初始状态下,第 i i i只蝴蝶在结点 a i a_i ai上。 a a a数组中的元素两两不同。
Koxia 要玩一个这样的游戏:
- 对于 i = 1 , 2 , 3 , . . . , n − 1 i = 1,2,3,...,n - 1 i=1,2,3,...,n−1,Koxia 将第 i i i条边的方向等概率随机地设定为 u i → v i u_i \to v_i ui→vi和 v i → u i v_i\to u_i vi→ui中的一种。
- 对于 i = 1 , 2 , 3 , . . . , n − 1 i = 1,2,3,...,n - 1 i=1,2,3,...,n−1,如果第 i i i条边的起点上有一只蝴蝶而其终点上没有,则这只蝴蝶会飞到终点。注意这一操作是以 1 , 2 , . . . , n − 1 1,2,...,n - 1 1,2,...,n−1的顺序依次进行而非同时的。
- Koxia 从 k k k只蝴蝶中选择两只,这一选择是在所有 k ( k − 1 ) 2 \frac{k(k - 1)}{2} 2k(k−1)种选择中等概率随机的。然后,她将选中的两只蝴蝶所在结点的距离作为她的得分。
现在,Koxia 希望你计算她的得分的期望值,输出其对 998244353 998244353 998244353取模后的结果 。 树上两个结点的距离是它们之间(唯一)的简单路径包含的边数。
solution:
考虑以下几个问题:
(1)若已知每只蝴蝶最后所在结点,求得分的期望。
期望是所有蝴蝶的距离之和除以方案数,及
E
=
∑
i
=
1
k
∑
j
=
1
k
[
i
≠
j
]
d
i
s
(
i
,
j
)
k
(
k
−
1
)
/
2
E = \frac{\sum_{i = 1}^k\sum_{j = 1}^k[i \neq j]dis(i,j)}{k(k - 1) / 2}
E=k(k−1)/2∑i=1k∑j=1k[i=j]dis(i,j)
对于
∑
i
=
1
k
∑
j
=
1
k
[
i
≠
j
]
d
i
s
(
i
,
j
)
\sum_{i = 1}^k\sum_{j = 1}^k[i \neq j]dis(i,j)
∑i=1k∑j=1k[i=j]dis(i,j)的计算是一个经典问题,求树上
k
k
k个点之间两两的距离和。
可以考虑每条边 ( u , v ) (u,v) (u,v),默认 u u u为 v v v的父亲,这条边的贡献是 s i z [ v ] ∗ ( k − s i z [ v ] ) siz[v]*(k - siz[v]) siz[v]∗(k−siz[v]),其中 s i z [ v ] siz[v] siz[v]为以 v v v为父节点的子树中含所求点的个数。
故贡献为
E
=
∑
(
u
,
v
)
∈
E
s
i
z
[
v
]
∗
(
k
−
s
i
z
[
v
]
)
k
(
k
−
1
)
/
2
E = \frac{\sum_{(u,v)\in E}siz[v]*(k - siz[v])}{k(k - 1) / 2}
E=k(k−1)/2∑(u,v)∈Esiz[v]∗(k−siz[v])
(2)考虑枚举到第i条边时,节点上有蝴蝶的概率
设 p i p_i pi表示当前时刻 i i i节点有蝴蝶的概率。开始时,给出 k k k个点上的 p p p值为 1 1 1,其他点 p p p值为 0 0 0。
考虑每次给边定向会对 p p p产生怎样的影响。
对边 ( u , v ) (u,v) (u,v),有 p u , p v p_u,p_v pu,pv。这条边随机定向后,有
若 u → v u\to v u→v,则 p u ′ = p v ∗ p u , p v ′ = p u + ( 1 − p u ) ∗ p v {p_u}^\prime = p_v * p_u,{p_v}^\prime = p_u + (1 - p_u)*p_v pu′=pv∗pu,pv′=pu+(1−pu)∗pv
若$v\to u,则{p_u}^\prime= p_v + (1 - p_v)*p_u,{p_v}^\prime = p_u * p_v $
故
p u ′ = p v ′ = p u + p v 2 {p_{u}}^\prime = {p_v}^\prime = \frac{p_u + p_v}{2} pu′=pv′=2pu+pv
解决上面两个问题之后就很好做了。只需要按顺序枚举边,计算边的贡献,并计算蝴蝶通过这条边转移对答案的贡献。由于每条边只会被枚举一次,根据题意,至多一只蝴蝶通过这条边转移,分以下几种情况。
- u,v点都有蝴蝶,无论边怎么定向, s i z [ v ] , k − s i z [ v ] siz[v],k - siz[v] siz[v],k−siz[v]不变。
- u点有蝴蝶,v点没有蝴蝶,且 u → v u\to v u→v, s i z [ v ] + 1 , ( k − s i z [ v ] ) − 1 siz[v]+1,(k - siz[v]) - 1 siz[v]+1,(k−siz[v])−1
- v点有蝴蝶,u点没有蝴蝶,且 v → u v\to u v→u, s i z [ v ] − 1 , ( k − s i z [ v ] ) + 1 siz[v]-1,(k - siz[v]) + 1 siz[v]−1,(k−siz[v])+1
故对边 ( u , v ) (u,v) (u,v),贡献为
s i z v ∗ ( k − s i z v ) + p u ∗ ( 1 − p v ) 2 ∗ ( − s i z v ∗ ( k − s i z v ) + ( s i z v + 1 ) ∗ ( k − s i z v − 1 ) ) + p v ∗ ( 1 − p u ) 2 ∗ ( − s i z v ∗ ( k − s i z v ) + ( s i z v − 1 ) ∗ ( k − s i z v + 1 ) ) siz_v *(k - siz_v) \\+ \frac{p_u * (1 - p_v)}{2}*(-siz_v*(k - siz_v)+(siz_v + 1)*(k - siz_v - 1)) \\+ \frac{p_v * (1 - p_u)}{2}*(-siz_v*(k - siz_v)+(siz_v - 1)*(k - siz_v + 1)) sizv∗(k−sizv)+2pu∗(1−pv)∗(−sizv∗(k−sizv)+(sizv+1)∗(k−sizv−1))+2pv∗(1−pu)∗(−sizv∗(k−sizv)+(sizv−1)∗(k−sizv+1))
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 300000 + 200
#define pii pair<int,int>
#define fi first
#define se second
int n,k;
int but[N],pa[N];
long long p[N],siz[N];
long long ans;
vector<int>r[N];
vector<pii> e;
const long long mod = 998244353;
long long qpow(long long x,long long t)
{
long long res = 1,tmp = x;
while(t)
{
if(t & 1)
res = res * tmp % mod;
tmp = tmp * tmp % mod;
t >>= 1;
}
return res % mod;
}
void dfs(int x,int fa)
{
if(p[x])
siz[x] = 1;
pa[x] = fa;
for(auto v:r[x])
{
if(v == fa)
continue;
dfs(v,x);
siz[x] += siz[v];
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i = 1;i <= k;i ++)
{
scanf("%d",&but[i]);
p[but[i]] = 1ll;
}
for(int i = 1;i <= n - 1;i ++)
{
int u,v;
scanf("%d %d",&u,&v);
r[u].push_back(v);
r[v].push_back(u);
e.push_back({u,v});
}
dfs(1,0);
long long inv2 = qpow(2ll,mod - 2);
for(int i = 0;i < n - 1;i ++)
{
int u = e[i].fi,v = e[i].se;
if(pa[u] == v)
swap(u,v);
long long puv = (p[u] * (1ll - p[v]) % mod + mod) % mod;
long long pvu = (p[v] * (1ll - p[u]) % mod + mod) % mod;
long long d = 0;
d = (d - puv * siz[v] % mod * (k - siz[v]) % mod) % mod;
d = (d + puv * (k - siz[v] - 1ll) % mod * (siz[v] + 1ll) % mod) % mod;
d = (d - pvu * siz[v] % mod * (k - siz[v]) % mod) % mod;
d = (d + pvu * (siz[v] - 1ll) % mod * (k - siz[v] + 1ll) % mod) % mod;
d = d * inv2 % mod;
d = (d + siz[v] * (k - siz[v])) % mod;
ans = ((ans + d) % mod + mod) % mod;
p[u] = p[v] = 1ll*((p[u] + p[v] % mod) * inv2) % mod;
}
ans %= mod;
ans = ans * (qpow((1ll*k * (1ll * k - 1ll) / 2ll) % mod,mod - 2) % mod) % mod;
printf("%lld\n",ans);
}