LOJ#3109「TJOI2019」甲苯先生的线段树

58 篇文章 0 订阅
6 篇文章 0 订阅

Address

LOJ#3109 洛谷P5342 BZOJ5513

Solution

  • 挺有意思的题。
  • 第一问就是暴力在树上跳,设答案为 s s s
  • 考虑第二问,单点的情况直接处理,设路径上深度最小的点为 x x x,先从简单的情况入手。

  • 对于从 x x x 往下走形成的长度为 h ( h > 1 ) h(h > 1) h(h>1) 的链,如果往下走的过程中都是往左孩子走,链的编号总和为 ∑ i = 0 h − 1 2 i x = ( 2 h − 1 ) x \sum \limits_{i = 0}^{h - 1}2^ix = (2^h - 1)x i=0h12ix=(2h1)x
  • 考虑把某一步改为往右孩子走,不难发现每个右孩子的贡献是独立的。把走到倒数第 i i i 个节点的那一步改为往右孩子走产生的贡献为 ∑ j = 0 i − 1 2 j = 2 i − 1 \sum \limits_{j = 0}^{i - 1}2^j = 2^i - 1 j=0i12j=2i1
  • 一个非常关键的结论:

s , h s,h s,h 已知时,若存在合法的 x x x x x x 为定值 ⌊ s 2 h − 1 ⌋ \begin{aligned} \lfloor \frac{s}{2^h - 1}\rfloor \end{aligned} 2h1s

  • 证明:
  1. 显然有 x ≤ ⌊ s 2 h − 1 ⌋ \begin{aligned} x \le \lfloor \frac{s}{2^h - 1} \rfloor \end{aligned} x2h1s
  2. x = ⌊ s 2 h − 1 ⌋ − 1 \begin{aligned} x = \lfloor \frac{s}{2^h - 1} \rfloor - 1 \end{aligned} x=2h1s1,全往右儿子走的编号和为
    s ′ = ( 2 h − 1 ) ( ⌊ s 2 h − 1 ⌋ − 1 ) + ∑ i = 1 h − 1 ( 2 i − 1 ) = ( 2 h − 1 ) ⌊ s 2 h − 1 ⌋ − h < s \begin{aligned} s' &= (2^{h} - 1)( \lfloor \frac{s}{2^{h} - 1} \rfloor - 1) + \sum \limits_{i = 1}^{h - 1}{(2^i - 1)}\\ &= (2^h - 1) \lfloor \frac{s}{2^h - 1} \rfloor - h <s\\ \end{aligned} s=(2h1)(2h1s1)+i=1h1(2i1)=(2h1)2h1sh<s 因此 x ≥ ⌊ s 2 h − 1 ⌋ \begin{aligned} x \ge \lfloor \frac{s}{2^h - 1} \rfloor \end{aligned} x2h1s
  3. 综上, x = ⌊ s 2 h − 1 ⌋ \begin{aligned} x = \lfloor \frac{s}{2^h - 1} \rfloor \end{aligned} x=2h1s
  • 现在问题转化为用 2 i − 1 ( 1 ≤ i < h ) 2^i - 1(1 \le i < h) 2i1(1i<h) 的和来表示 s − ( 2 h − 1 ) x s - (2^h - 1)x s(2h1)x,又因为 2 i − 1 > ∑ j = 1 i − 1 ( 2 j − 1 ) 2^i - 1 > \sum \limits_{j = 1}^{i - 1}(2^j - 1) 2i1>j=1i1(2j1),直接从大到小枚举 i i i,贪心填入即可。

  • 考虑 x x x 同时有左孩子和右孩子的情况,设从左孩子往下走所形成的链(包含点 x x x)的长度为 h 1 ( h 1 ≥ 2 ) h_1(h_1 \ge 2) h1(h12),从右孩子往下走所形成的链(包含点 x x x)的长度为 h 2 ( h 2 ≥ 2 ) h_2(h_2 \ge 2) h2(h22)
  • 同样地,若往下走的过程中都是往左孩子走,路径的编号和 = 2 x ( 2 h 1 − 1 − 1 ) + ( 2 x + 1 ) ( 2 h 2 − 1 − 1 ) + x = ( 2 h 1 + 2 h 2 − 3 ) x + 2 h 2 − 1 − 1 \begin{aligned} & = 2x(2^{h_1 - 1}-1) + (2x+1)(2^{h_2 - 1} - 1) + x\\ & = (2^{h_1} + 2^{h_2} - 3)x + 2^{h_2 - 1} - 1\\ \end{aligned} =2x(2h111)+(2x+1)(2h211)+x=(2h1+2h23)x+2h211
  • 同理可证:

s , h 1 , h 2 s, h_1, h_2 s,h1,h2 固定时,若存在合法的 x x x x x x 为定值 ⌊ s − 2 h 2 − 1 + 1 2 h 1 + 2 h 2 − 3 ⌋ \begin{aligned} \lfloor \frac{s - 2^{h_2 - 1} + 1}{2^{h_1} + 2^{h_2} - 3}\rfloor \end{aligned} 2h1+2h23s2h21+1

  • 现在问题转化为在 2 i 1 − 1 ( 1 ≤ i 1 ≤ h 1 − 2 ) 2^{i_1} - 1(1 \le i_1 \le h_1 - 2) 2i11(1i1h12) 以及 2 i 2 − 1 ( 1 ≤ i 2 ≤ h 2 − 2 ) 2^{i_2} - 1(1 \le i_2 \le h_2 - 2) 2i21(1i2h22) 中选取若干个数字来表示 s − ( 2 h 1 + 2 h 2 − 3 ) s − 2 h 2 + 1 s - (2^{h_1} + 2^{h_2} - 3) s - 2^{h_2} + 1 s(2h1+2h23)s2h2+1,显然不能再像上面那样贪心了。
  • 枚举选出的数字个数 c n t cnt cnt,把问题进一步转化为在 2 i 1 ( 1 ≤ i 1 ≤ h 1 − 2 ) 2^{i_1} (1 \le i_1 \le h_1 - 2) 2i1(1i1h12) 2 i 2 ( 1 ≤ i 2 ≤ h 2 − 2 ) 2^{i_2} (1 \le i_2 \le h_2 - 2) 2i2(1i2h22) 中选取若干个数字来表示 v = s − ( 2 h 1 + 2 h 2 − 3 ) s − 2 h 2 + c n t + 1 v = s - (2^{h_1} + 2^{h_2} - 3) s - 2^{h_2} + cnt + 1 v=s(2h1+2h23)s2h2+cnt+1
  • 因为 2 i 2^i 2i 可以看成是二进制位上的 +1,考虑 数位DP
  • f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示从第低到高处理到第 i i i 位、已经选了 j j j 个数字且对第 i + 1 i + 1 i+1 位是/否有进位 ( k = 0 / 1 ) (k = 0/1) (k=0/1) 时的方案数,容易得到转移: f [ i + 1 ] [ j + a + b ] [ ⌊ k + a + b 2 ⌋ ] + = f [ i ] [ j ] [ k ] \begin{aligned} f[i + 1][j + a + b][\lfloor \frac{k + a + b}{2} \rfloor] += f[i][j][k]\\ \end{aligned} f[i+1][j+a+b][2k+a+b]+=f[i][j][k]
  • 其中 a , b a, b a,b 表示第 i i i 位在 2 i 1 ( 1 ≤ i 1 ≤ h 1 − 2 ) 2^{i_1}(1 \le i_1 \le h_1 - 2) 2i1(1i1h12) 2 i 2 ( 1 ≤ i 2 ≤ h 2 − 2 ) 2^{i_2}(1 \le i_2 \le h_2 - 2) 2i2(1i2h22) 中是否选了数,需要根据 h 1 , h 2 h_1,h_2 h1,h2 进行限制,并且保证 ( k + a + b ) m o d    2 (k + a + b) \mod 2 (k+a+b)mod2 的值和 v v v 的第 i i i 位相同。

  • 总的时间复杂度 O ( T d 5 ) \mathcal O(Td^5) O(Td5)

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
}

template <class T>
inline void put(T x)
{
	if (x > 9)
		put(x / 10);
	putchar(x % 10 + 48);
}

typedef long long ll;
const int N = 55;
const int M = 105;
int T, d, c;
ll a, b, s, f[N][M][2];

template <class T>
inline void CkMin(T &x, T y) {x > y ? x = y : 0;}
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}

inline int askD(ll x)
{
	int cnt = 0;
	while (x)
		x >>= 1, ++cnt;
	return cnt;
}

inline ll askVal()
{
	int da = askD(a), db = askD(b);
	if (da > db)
		std::swap(a, b), std::swap(da, db);
	ll ans = 0;
	while (db > da)
	{
		ans += b;
		b >>= 1;
		--db;
	}
	while (a != b)
	{
		ans += a + b;
		a >>= 1;
		b >>= 1;
	}
	return ans + a;
}

inline int solve1()
{
	int ans = 0;
	for (int h = 2; h <= d; ++h)
	{
		ll x = s / ((1ll << h) - 1);
		if (!x)
			break ;
		if (askD(x) + h - 1 > d)
			continue ;
		ll delt = s - ((1ll << h) - 1) * x;
		for (int j = h - 1; j >= 1; --j)
			if (delt >= (1ll << j) - 1)
				delt -= (1ll << j) - 1;
		!delt ? ++ans : 0;
	}
	return ans;
}

inline ll solve2()
{
	ll res = 0;
	for (int h1 = 2; h1 <= d; ++h1)
		for (int h2 = 2; h2 <= d; ++h2)
		{
			if (s - (1ll << h2 - 1) + 1 <= 0)
				break ;
			ll x = (s - (1ll << h2 - 1) + 1) / ((1ll << h1) + (1ll << h2) - 3);
			if (!x)
				break ;
			if (askD(x) + Max(h1, h2) - 1 > d)
				continue ;
			ll delt = s - ((1ll << h1) + (1ll << h2) - 3) * x - (1ll << h2 - 1) + 1;
			int tot = h1 + h2 - 4;
			for (int cnt = 0; cnt <= tot; ++cnt)
				if (!(delt + cnt & 1))
				{
					delt += cnt;
					int ds = askD(delt) + 1;
					for (int i = 1; i <= ds + 1; ++i)
						for (int j = 0, jm = i - 1 << 1; j <= jm; ++j)
							f[i][j][0] = f[i][j][1] = 0;	
					f[1][0][0] = 1;
					for (int i = 1; i <= ds; ++i)
						for (int j = 0, jm = Min(i - 1 << 1, tot); j <= jm; ++j)
							for (int k = 0; k < 2; ++k)
								if (f[i][j][k])
								for (int a = 0, am = i < h1 - 1 ? 1 : 0; a <= am; ++a)
									for (int b = 0, bm = i < h2 - 1 ? 1 : 0; b <= bm; ++b)
										if ((k + a + b & 1) == (delt >> i & 1))
											f[i + 1][j + a + b][k + a + b >> 1] += f[i][j][k];
					res += f[ds + 1][cnt][0];
					delt -= cnt;
				}
		}
	return res;

}

int main()
{
	read(T);
	while (T--)
	{
		read(d); 
		read(a);
		read(b);
		read(c);

		s = askVal();
		put(c == 1 ? s : solve1() + solve2() - (s >= (1ll << d))), putchar('\n');
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值