__int128用法(例题:2024牛客多校9的I题)

题目

小G正在寻找一种特殊类型的数字,它具有以下特征:它们有 n n n位数字。将这个数除以两半得到两个完全平方数,可能有前导零。可以保证 n ≡ 0   m o d   2 n \equiv 0 \bmod 2 n0mod2,(这个的意思其实就是,n一定是偶数,即一定能对半分)

现在小G想知道在 [ L , R ] [L, R] [L,R]范围内有多少这样的数。

这两半的长度必须相同。

输入

1 1 1:一个整数 n n n
2 2 2:两个整数 L L L R R R

数据范围

1 ≤ n ≤ 60 1\leq n\leq 60 1n60, 1 0 n − 1 ≤ L ≤ R < 1 0 n 10^{n-1} \le L \le R < 10^n 10n1LR<10n.

思路

这题的思路其实很简单,主要难点在与数据范围 1 0 60 10^{60} 1060以及远远超于long long的范围了,所以在这,要么python,要么开__int128。
以及,在这里还有个点药注意,因为 1 0 30 10^{30} 1030已经超过了signed double的范围(52位数, 2 52 2^{52} 252=4.5e16左右),所以这里的sqrt会出现向上取整的情况。
例如16个9,因为已经超signed double了,所以它不会以16个9的方式存入,而是会以,1e16 -1的方式存入,这个时候,开方就是1e8,但实际上开方出来为1e8-1。因此,这里的开放要手写二分

__int128

__int128 就是占用128字节的整数存储类型。由于是二进制,范围就是 -2127~2127-1,如果使用了 unsigned __int128,则范围变成 00 ~ 21282128,即约39位数,这在一定程度上可以替代高精度运算实现大数运算,而且操作难度更低,所以在数据范围不超过的情况下,都可以使__int128。

操作

_int128只能实现四则运算,不能用cin,cout,scanf,printf输入输出,所以需要用到写个快读和快写的函数;

快读

__int128 read()
{
	//直接在函数里面实现读字符串操作更简洁
	__int128 res=0;//初始结果赋值0
	char scan[1005];
	scanf("%s",scan);
	for(int i=0;i<strlen(scan);i++)
		res*=10,res+=scan[i]-'0';//实现进位
	return res;//返回__int128类型
}

快写

void print(__int128 num)
{//递归调用,实现从高位向低位输出
	if(num>9) 
		print(num/10);
	putchar(num%10+'0');
}

sqrt问题

1.要么特判

x = sqrt(num-1//或者
if(x * x > num) x=x-1

2.要么手写二分

__int128_t sqr(__int128_t x) {
	__int128_t l = 0, r = 4e15;
	while (r > l) {
		__int128_t mid = (l + r + 1) / 2;
		if (mid * mid > x) r = mid - 1;
		else l = mid;
	}
	return l;
}

代码

#include<bits/stdc++.h>
using namespace std;
#define i128 __int128
#define int long long
void print(__int128 num) {
	//递归调用,实现从高位向低位输出
	if(num>9)
		print(num/10);
	putchar(num%10+'0');

}
__int128_t sqr(__int128_t x) {
	__int128_t l = 0, r = 4e15;
	while (r > l) {
		__int128_t mid = (l + r + 1) / 2;
		if (mid * mid > x) r = mid - 1;
		else l = mid;
	}
	return l;
}


i128 qiu(i128 x,i128 y) {
	i128 res=0;
	i128 xx=sqr(x);
	i128 yy=sqr(y);
	res+=yy-xx;
	if(xx*xx>=x) {
		res+=1;
	}
	return res;
}
void solve() {
	int n;
	char C;
	cin>>n;
	i128 a=0,b=0,c=0,d=0,maxn=0;
	for(int i=0; i<n/2; i++) {
		cin>>C;
		a*=10;
		a+=C-'0';
	}
	for(int i=0; i<n/2; i++) {
		cin>>C;
		b*=10;
		b+=C-'0';
	}
	for(int i=0; i<n/2; i++) {
		cin>>C;
		c*=10;
		c+=C-'0';
	}
	for(int i=0; i<n/2; i++) {
		cin>>C;
		d*=10;
		d+=C-'0';
	}
	for(int i=0; i<n/2; i++) {
		maxn*=10;
		maxn+=9;
	}

	i128 ans=0;
	if(a==c) {

		if((sqr(a)*sqr(a))==a) {
			print(qiu(b,d));
		} else {
			cout<<0;
		}
		return;
	}
	if((sqr(a)*sqr(a))==a) {
		ans+=qiu(b,maxn);
	}
	if((sqr(c)*sqr(c))==c) {
		ans+=qiu(0,d);
	}
	ans+=qiu(a+1,c-1)*qiu(0,maxn);
	print(ans);
	cout<<"\n";
}

signed main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int t = 1;
//	cin >> t;
	while(t--) solve();
	return 0;
}



附上大佬的解释

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值