首页 > 其他分享 >【题解】CF1151C Problem for Nazar(二分答案)

【题解】CF1151C Problem for Nazar(二分答案)

时间:2022-10-18 15:34:59浏览次数:80  
标签:奇数 int 题解 Nazar sum2 times 偶数 CF1151C mod

【题解】CF1151C Problem for Nazar

距离 CSP 剩下 10 天了,据说考前写题解可以增加 RP所以我来写一篇题解+水点贡献分

看题解区没有用二分答案来解决这道题的,我来提供一个二分做法。


题目链接

CF1151C Problem for Nazar

题意概述

设正奇数集合为 \(\mathrm{A}\),正偶数集合为 \(\mathrm{B}\),这两个集合是无限集。

在黑板上写了无数轮数,第 \(i\) 轮写下了 \(2^{(i-1)}\) 个数.

当\(i\)为奇数时,从集合 \(\mathrm{A}\) 中向后取数,当 \(i\) 为偶数时,从集合 \(\mathrm{B}\) 中向后取数。

求黑板上第 \(l\) 个数到第 \(r\) 个数的和,模 \(\mathrm{1000000007}\)(\(10^9+7\))

数据范围

\(1 \le l,r \le 10^{18}\)

思路分析

首先看到这个数据范围,可以考虑到时间复杂度大概率是 \(\log n\) 这个级别的。

求的是下标为区间 \([l,r]\) 的和,那我们很自然联想到差分:可以先把区间 \([1,r]\) 的和求出来减去区间 \([1,l-1]\) 的和。

那么问题就转化为怎么求出前 \(x\) 个数的和。

观察题目条件可以发现,黑板上的数无非就是一串奇数+一串偶数+一串奇数……这样排列的。

相邻的两串奇数之间一定是连续的,也就是假如这一串奇数的结尾是 \(k\),那么与它相邻的下一串奇数的第一个数一定是 \(k+2\)。

相邻的两串偶数之间也同理。

注意:这里的相邻的两串奇数与相邻的两串数含义不同,每相邻的两串奇数之间隔着一串偶数。

那么我们能否把前 \(x\) 个数的和转化为所有的奇数串之和加上所有的偶数串之和呢?

答案是肯定的。

考虑最终形成的序列一定是这样的:

\(2^0\) 个奇数+\(2^1\) 个偶数+\(2^2\) 个奇数+\(2^3\) 个偶数+ \(\cdots\) +\(2^{(i-1)}\) 个不知道是奇数还是偶数的一串数+某个数 \(k\) 到 \(x\)。

很显然第 \(i\) 串数的奇偶性取决于 \(i\) 的奇偶性,并与 \(i\) 的奇偶性相同。

这里的 \(k\) 是第 \(i-1\) 串数的最后一个数 +2。

那么我们其实可以考虑把前 \(i\) 串数的和求出来,然后再单独把从 \(k\) 到 \(x\) 的和求出来然后相加。

那么我们首先要求出这个 \(i\)。

因为前 \(i\) 个串的长度和为 \(2^0+2^1+2^2+\cdots+2^{(i-1)}=2^i-1\),所以我们可以对于每个 \(x\) 直接二分求出这个 \(i\) 是多少。

具体代码如下:

int now=0,ret=0;
for(int step=(1ll<<60);step>=1;step>>=1)
{
	if(now+step<=60&&(1ll<<(now+step))-1<x)now+=step;
}

(这里采用的是倍增二分)

最后得到的这个 \(now\) 就是我们所说的 \(i\)。

由于前面已经提到过,相邻的两串奇数/偶数之间都是连续的。

那我们可以直接将前 \(i\) 串数的和分成奇数列和偶数列来求。

显然,对于前 \(i\) 串数,有 \(i/2\) 串偶数列,有 \(i-i/2\) 串是奇数列。

那么我们可以预处理出来奇数列和偶数列单独的和。

用 \(sum1_i\) 表示整个序列中前 \(i\) 串奇数列中有多少个数,\(sum2_i\) 表示整个序列中前 \(i\) 串偶数列中有多少个数,预处理如下:

void init()
{
	int cnt1=0,cnt2=0;
	for(int i=1;i<=60;i++)//之所以是 60,因为 2^60-1 已经大于 1e18 了,所以最多有 60 串数。
	{
		if(i&1)
		{
			cnt1++;
			sum1[cnt1]=sum1[cnt1-1]+(1ll<<(i-1));
		}
		else
		{
			cnt2++;
			sum2[cnt2]=sum2[cnt2-1]+(1ll<<(i-1));
		}
	}
}

那么对于前 \(i\) 串数,有 \(sum2[i/2]\) 个偶数,\(sum1[i-i/2]\) 个奇数。

接下来要利用一些等差数列的知识:

  • 等差数列第 \(n\) 项为 \(a_1+nd\)(\(a_1\) 表示首项,\(d\) 表示公差)。

  • 等差数列前 \(n\) 项的和为 \(\frac{(a_1+a_n)*n}{2}\)。

那么第 \(sum2[i/2]\) 个偶数就为 \(sum2[i/2] \times 2\),第 \(sum1[i-i/2]\) 个奇数就为 \(sum1[i-i/2] \times 2-1\)。

所以前 \(i\) 串数中偶数列之和为 \(s_1=\frac{(2+sum2[i/2]\times 2) \times sum2[i/2]}{2}\),奇数列之和为 \(s2=\frac{(1+sum1[i-i/2]*2-1) \times sum1[i-i/2]}{2}\)

然后对于最后 \(k\) 到 \(x\) 的那一段和,可以先把 \(k\) 求出来:当 \(i\) 为奇数时,\(k=sum2[i/2]\times 2+2\),当 \(i\) 为偶数时,\(k=sum1[i-i/2] \times 2+2\)。然后再把 \(x\) 求出来:当 \(i\) 为奇数时,\(k=sum2[i/2] \times 2+(x-2^i+1)\times 2\),当 \(i\) 为偶数时,\(k=sum1[i-i/2] \times 2+(x-2^i+1)\times 2\)。

然后从 \(k\) 到 \(x\) 也可以直接等差数列求和,设结果为 \(s\)。

那么最后答案就是 \(s1+s2+s\)。

易错点

别忘了及时取模以及负数保护!!!防止爆 long long!我在这挂了 2h……老毛病又犯了。。

代码实现

//CF1151C
#include<cstdio>
#include<iostream>
#define int long long
//#define debug
using namespace std;
const int maxn=105;
const int mod=1e9+7;
int sum1[maxn],sum2[maxn],n;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

void init()
{
	int cnt1=0,cnt2=0;
	for(int i=1;i<=60;i++)
	{
		if(i&1)
		{
			cnt1++;
			sum1[cnt1]=sum1[cnt1-1]+(1ll<<(i-1));
		}
		else
		{
			cnt2++;
			sum2[cnt2]=sum2[cnt2-1]+(1ll<<(i-1));
		}
	}
}

int work(int x)
{
	int now=0,ret=0;
	for(int step=(1ll<<60);step>=1;step>>=1)
	{
		if(now+step<=60&&(1ll<<(now+step))-1<x)now+=step;
	}
	int L=now;
	int re=x-(1ll<<L)+1;
	int tt=L>>1;
	int num=sum2[tt];
	int tot1=num*2;
	int s1=(2+tot1)/2%mod*(num%mod)%mod;
	int kk=L-tt;
	num=sum1[kk];
	int tot2=num*2-1;
	int s2=(1+tot2)/2%mod*(num%mod)%mod;
	if(L&1)
	{
		(ret=(s1+s2)%mod+(tot1+2+tot1+re*2)/2%mod*(re%mod)%mod)%=mod;
	}
	else
	{
		(ret=(s1+s2)%mod+(tot2+2+tot2+re*2)/2%mod*(re%mod)%mod)%=mod;
	}
	return ret;
}

signed main()
{
	init();
	int l,r;
	l=read();r=read();
	cout<<(work(r)-work(l-1)+mod)%mod<<'\n';
	return 0;
}

标签:奇数,int,题解,Nazar,sum2,times,偶数,CF1151C,mod
From: https://www.cnblogs.com/xrkforces/p/CF1151C.html

相关文章

  • CF641E 题解
    前言题目传送门!更好的阅读体验?非常套路的cdq分治。思路把所有操作统一存下来。将\(x\)离散化。\(i\)能被\(j\)统计,前提是\(a_i\)的操作时间早于\(a_j\)。......
  • 蓝桥杯第二次模拟赛JAVA题解
    目录​​第一题......
  • Codeforces Round #827 (Div. 4) 复盘+题解
    原比赛链接复盘:ABC签到,手速太慢了。D捣鼓了好久才想起来从更小的值域出发去做。E简单二分答案。然后就timeout了。D题搞错方向浪费太久时间了。F思维题,拐两个弯再$r......
  • P7868 VUDU 题解
    P7868VUDU题解提供一种不需要使用离散化,从\(0/1\)分数规划的角度推导的思路。然而考试的时候没想到求逆序对挂掉了分析题意很清楚了,就是求给出的序列中,对于一段任意长......
  • CF309E 题解
    11:30,过题。12:50,忘记做法。吃饭时不该看未来日记的,Ynoj害人不浅(确信)。以上为个人吐槽。题目大意不知道题目翻译是个啥。。。但讨论区有大佬给出了精确的翻译。我改得......
  • AcCoders 10692:【2022NOIP联测10 10月17日】交换(swap) 题解
    考虑把一次交换产生的贡献记录在交换的两个数字中较小的那个数字上。则构造一个好的序列的过程可以看成是:按照从小到大的顺序枚举每个数,每次选择将这个数放在序列的左边或......
  • POJ 3760. 魔兽世界(修订版) 题解
    一句话,大模拟,照着题意敲就完了。写的期间甚至因为疫情导致程序被锁在了机房www//3760.魔兽世界(修订版)#include<iostream>#include<cstring>#include<string>u......
  • 【题解】CF11D A Simple Task(状压 DP)
    【题解】CF11DASimpleTask题目链接CF11DASimpleTask题意概述给定一张\(n\)个点\(m\)条边的无向图,无重边自环,点数不超过\(19\),求无向图中环的数量。思路分......
  • [题解] Atcoder Regular Contest ARC 151 A B C D E 题解
    点我看题昨天刚打的ARC,题目质量还是不错的。A-EqualHammingDistances对于一个位置i,如果\(S_i=T_i\),那么不管\(U\)的这个位置填什么,对到\(S\)和\(T\)的海明距离增量......
  • CF463C 题解
    题目传送门题目分析贪心练手好题。首先,国际象棋中象的走法是斜着走,也就是这样:通过上面的图我们不难看出,如果一个象在黑格,另外一个在白格,那么它们之间一定不会互相攻击......