Codeforces Round #818 (Div. 2)

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=0kCni

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=nlcm(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,nc)
我们设 g c d ( a , n − c ) = d gcd(a,n-c)=d gcd(a,nc)=d,那么 g c d ( a d , n − c d ) = 1 gcd(\frac{a}{d},\frac{n-c}{d})=1 gcd(da,dnc)=1
因为 a + b = n − c a+b=n-c a+b=nc,所以 a d < n − c d \frac{a}{d}<\frac{n-c}{d} da<dnc 并且 a d \frac{a}{d} da n − c d \frac{n-c}{d} dnc 互质
a d \frac{a}{d} da 的数量为欧拉函数 ϕ ( n − c d ) ϕ(\frac{n-c}{d}) ϕ(dnc) (欧拉函数定义)
而我们可以 O ( n ) O(n) O(n) 求出 1 到 n 的欧拉函数 (欧拉筛)
但是如果我们如果枚举 c c c,再质因数分解 n − c n-c nc
我们的复杂度为 O ( n n l o g n ) O(n\sqrt n logn) O(nn logn) ( l o g n logn logn g c d gcd gcd的复杂度)
但如果我们枚举 d d d,再枚举 n − c n-c nc ( n − c n-c nc 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<nc 所以 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 大佬说是用网络流过的,还没学,留个坑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值