D Madoka and The Corruption Scheme
我们可以将所有的比赛看成有
2
n
2^n
2n 个叶子节点的二叉树
问题转换为对每个非叶子结点规定左/右儿子赢,并规定叶子节点编号
使得在修改k个非叶子节点的状态后最后赢家编号最小
那么我们可以先约定每个非叶子节点的左儿子赢
从根节点到叶子节点经过n条边
我们规定从当前节点走向左儿子代表一个字符‘L’
另一边为字符‘R’
那么每个叶子节点代表一个LR序列
如果想要一个叶子节点成为最后赢家,那么我们需要修改所有R的位置
那么代表叶子结点的修改权值为R的数量
如果修改次数为k,那么所有LR序列中R的数量小于等于k的都可以成为最后赢家
LR序列中R的个数为
i
i
i 的有
C
n
i
C_n^i
Cni 个
那么我们贪心的对修改权值从小到大的放入1到
2
n
2^n
2n
答案即为
∑
i
=
0
k
C
n
i
\sum\limits_{i=0}^{k}C_n^i
i=0∑kCni
PS:
赛时其实是画二叉树找规律找出来的,本来是想贪心的构造一个序列
但是在计算了一下每个叶子节点的权值时发现了经典组合数
1
3
3
1
1\quad 3\quad 3\quad 1
1331
之后粘了个组合数板子就秒了,启发就是以后可以计算节点地位或者说节点的权值
然后放置编号,那么思路会明确的多
ll n, m, k;
namespace cnm
{
const int FN = 1e5 + 5;
ll fac[FN];//阶乘数组
ll ifc[FN];//阶乘逆元
ll inv[FN];//逆元
void init(int n)
{
fac[0] = fac[1] = ifc[0] = ifc[1] = inv[1] = 1;
rep(i, 2, n)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
ifc[i] = ifc[i - 1] * inv[i] % mod;
}
}
ll A(ll n, ll m)
{ return fac[n] * ifc[n - m] % mod; }
ll C(ll n, ll m)
{
if (n < m) return 0;
return fac[n] * ifc[m] % mod * ifc[n - m] % mod;
}
ll lucas(ll n,ll m)//当n,m超过1e5,mod比较小时
{
if (n < mod && m < mod) return C(n, m);
return (C(n % mod, m % mod) * lucas(n / mod, m / mod)) % mod;
}
}
using cnm::C; using cnm::A; using cnm::lucas; using cnm::fac; using cnm::ifc; using cnm::inv;
void solve()
{
cin >> n >> k;
if (k >= n) cout << ksm(2ll, n, mod) << endl;
else
{
cnm::init(n);
ll ans = 0;
rep(i, 0, k) ans = (ans + C(n, i)) % mod;
cout << ans << endl;
}
}
E Madoka and The Best University
这题赛时没过,赛后补的
一开始不会欧拉筛,先去学了下欧拉筛
题目是求
∑
a
+
b
+
c
=
n
l
c
m
(
c
,
g
c
d
(
a
,
b
)
)
\sum\limits_{a+b+c=n}lcm(c,gcd(a,b))
a+b+c=n∑lcm(c,gcd(a,b))
首先看题解我们知道了
g
c
d
(
a
,
b
)
=
g
c
d
(
a
,
a
+
b
)
=
g
c
d
(
a
,
n
−
c
)
gcd(a,b)=gcd(a,a+b)=gcd(a,n−c)
gcd(a,b)=gcd(a,a+b)=gcd(a,n−c)
我们设
g
c
d
(
a
,
n
−
c
)
=
d
gcd(a,n-c)=d
gcd(a,n−c)=d,那么
g
c
d
(
a
d
,
n
−
c
d
)
=
1
gcd(\frac{a}{d},\frac{n-c}{d})=1
gcd(da,dn−c)=1
因为
a
+
b
=
n
−
c
a+b=n-c
a+b=n−c,所以
a
d
<
n
−
c
d
\frac{a}{d}<\frac{n-c}{d}
da<dn−c 并且
a
d
\frac{a}{d}
da 与
n
−
c
d
\frac{n-c}{d}
dn−c 互质
即
a
d
\frac{a}{d}
da 的数量为欧拉函数
ϕ
(
n
−
c
d
)
ϕ(\frac{n-c}{d})
ϕ(dn−c) (欧拉函数定义)
而我们可以
O
(
n
)
O(n)
O(n) 求出 1 到 n 的欧拉函数 (欧拉筛)
但是如果我们如果枚举
c
c
c,再质因数分解
n
−
c
n-c
n−c
我们的复杂度为
O
(
n
n
l
o
g
n
)
O(n\sqrt n logn)
O(nnlogn) (
l
o
g
n
logn
logn为
g
c
d
gcd
gcd的复杂度)
但如果我们枚举
d
d
d,再枚举
n
−
c
n-c
n−c (
n
−
c
n-c
n−c为
d
d
d的倍数)
我们的复杂度就降为
O
(
(
n
l
o
g
l
o
g
n
)
l
o
g
n
)
O((nloglogn)logn)
O((nloglogn)logn)
(
l
o
g
n
logn
logn为
g
c
d
gcd
gcd的复杂度,
n
l
o
g
l
o
g
n
nloglogn
nloglogn为埃氏筛的复杂度)
特别要注意的是
a
<
n
−
c
a<n-c
a<n−c 所以
d
d
d 取不到 1
我们直接让欧拉函数
p
h
i
[
1
]
=
0
phi[1]=0
phi[1]=0 即可(本来是等于1的)
ps:
感觉数学题中
l
c
m
lcm
lcm 和
g
c
d
gcd
gcd 很容易满足积性函数的性质
之后遇到类似的题目,如果没有思路,可以猜测是否为积性函数
int n, m, k;
bool np[N];//非质数
int p[N], pn;//质数 质数个数
template<typename T>
void get_f(int n, T f[])
{
f[1] = 0;
rep(i, 2, n)
{
if (!np[i]) p[++pn] = i, f[i] = i - 1;
rep(j, 1, pn)
{
if (i * p[j] > n) break;
np[i * p[j]] = true;
if (i % p[j] == 0)
{
f[i * p[j]] = f[i] * p[j];
break;
}
f[i * p[j]] = f[i] * (p[j] - 1);
}
}
}
int phi[N];
void solve()
{
cin >> n;
get_f(n, phi);
ll ans = 0;
for (ll d = 1; d < n; d++)
{
for (ll i = 1; i * d < n; i++)
{
ll c = n - i * d;
ans += c * d / gcd(c, d) * phi[i] % mod;
ans %= mod;
}
}
cout << ans << endl;
}
F 大佬说是用网络流过的,还没学,留个坑