ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【CF768B】Code For 1 题解

2022-07-20 08:34:12  阅读:203  来源: 互联网

标签:10 Code int 题解 ch 二进制 lst 01 CF768B


传送门:B. Code For 1

像我这么菜的 \(Oier\),考场上只想的出来类似模拟线段树的做法啦~

被暴力吊打(大哭)。此题解提供一种新做法做参考。

Solution

1

首先我们发现一个给定的 \(n\),我们是可以求出它最终化为的 01 串的长度。

设 \(s_{lst}\) 为当前分解得到的串的长度,\(x\) 为分解出的、用二进制表示的 01 “串”长度最大的一个数,\(y\) 为长度最大的数的个数。

例如,当输入的 \(n\) 为 10 时,二进制下表示为 1010,此时 \(s_{lst}\) 为 4,\(x\) 为 4, \(y\) 为 1。

将 10 分解成 5,0,5,即二进制下的 101,0,101,此时 \(s_{lst}\) 为 7,\(x\) 为 3,\(y\) 为 2。

进一步分解,2,1,2,0,2,1,2,即二进制下的 10,1,10,0,10,1,10,此时 \(s_{lst}\) 为 11,\(x\) 为 2,\(y\) 为 4。

综上,我们可以得到:假设我们已有 \(s_{lst},\ x,\ y\),想得到分解一次之后的状态的 \(s_{now},\ x',\ y'\)。

有:\(s_{now}=((x-1)* 2+1) * y + (s_{lst}-x* y)\)

其中 \(((x-1)* 2+1)\) 是每一个长度为 \(x\) 的数分解出来的新的三个数 二进制下 01 串拼起来的总长度。比如说 5(\((101)_2\))分解成 2,1,2,这三个数二进制下的 01 串拼起来就是 10110,长度为 5。

\((s_{lst}-x* y)\) 表示除去那些二进制表示下长度为 \(x\) 的数,剩下的、值为 0 或 1 的数的个数。因为它们不会被再次分解,所以直接保留即可。

其余的:\(x' \gets (x - 1),\ y' \gets (y * 2)\)。这个不难理解。

一直计算出新的 \(s\),直到 \(x = 1\),说明此时每个数二进制表示下的长度都是 1 了,即每个数的值非 0 即 1,分解结束。

一顿操作下来,不会浪费多少时间(因为 \(n\) 本身不超过 2 的 50 次方)。

2

建树部分。

我们现在有 \(n=10\),把它分解后的状态拆成一个树:

节点编号:

  1
 / \
2   3

每个节点代表的权值:

  0
 / \
5   5

所代表的二进制下的数:

   0
 /   \
101  101

每个节点代表的(维护的)最终的 01 串的区间:

(10 分解到最后,01 串的长度为 15。)

     (1~15)
    /       \
(1~7) (9~15)

(8 去哪里了?1 号节点本身就代表着八号节点。)

在上面这么多图中,我们发现 2 号节点和 3 号节点其实是一模一样的,所以我们只需要建 2 号节点一个就可以了。

不难发现这颗树最后的深度就是 \(n\) 在二进制表达下的 01 串的长度。

3

啊我们发现查询的最后结果就是该区间包含的数的总数量减去值为 0 的数的数量。

所以我们的树就是维护区间内值为 0 的数的数量。

查询的时候要注意如果查询范围包括了我们实际上没有建的右子树,我们要把它转化为查询左子树的对应区间。

Code

注意特判 0!!!

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 55;
int n, tp;
int n2[maxn], tot;
int cnt;
int ans;
int x, y, s;
int ql, qr;
struct node{
	int l, r, val, num;
	int sum;
	int ch;
}t[10005];

inline int rd()
{
	int s = 0, x = 1;
	char ch = getchar();
	while(ch < '0' or ch > '9') {if(ch == '-') x = -1; ch = getchar();}
	while(ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return x * s;
}

inline int build(int l, int r)
{
	if(cnt == tot or l > r) return 0;
	int u = ++cnt;
	t[u].l = l, t[u].r = r;
	t[u].val = (n2[u] ? 0 : 1), t[u].num = l + r >> 1;
	t[u].ch = build(l, (l + r >> 1) - 1);
	t[u].sum = t[t[u].ch].sum * 2 + t[u].val;
	return u;
}

inline int query(int i, int l, int r)
{
	if(t[i].l >= l and t[i].r <= r) return t[i].sum;
	if(l > r) return 0;
	int ts = 0;
	if(t[i].num >= l and t[i].num <= r) ts = t[i].val;
	if(l < t[i].num) ts += query(t[i].ch, l, min(t[i].num - 1, r));
	if(r > t[i].num) 
	{
		int ll = max((long long)1, l - t[i].num), rr = r - t[i].num;
		ts += query(t[i].ch, ll, rr);
	}
	return ts;
}

signed main()
{
//	freopen("E:/in49.txt", "r", stdin);
	tp = n = rd();
	if(!n)
	{
		printf("0\n");
		return 0;
	}
	while(tp > 0) {n2[++tot] = tp % 2, tp /= 2;}
	s = x = tot, y = 1;
	while(x > 1) {s = ((x - 1) * 2 + 1) * y + s - x * y, x -= 1, y *= 2;}
	ql = rd(), qr = rd();
	build(1, s);
	ans = qr - ql + 1, ans -= query(1, ql, qr);
	printf("%lld\n", ans);
	return 0;
}
/*1125899906842624*/

感谢阅读。

辛苦管理员审核,如有问题烦请指出。

标签:10,Code,int,题解,ch,二进制,lst,01,CF768B
来源: https://www.cnblogs.com/gsn531/p/16496501.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有