【CF768B】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