首页 > 其他分享 >题解:P10815 【模板】快速读入

题解:P10815 【模板】快速读入

时间:2024-07-30 08:55:34浏览次数:7  
标签:10 ch putchar P10815 int 题解 cout 读入 getchar

闲着没事儿水篇 tj


题目大意

题目大意极其粗暴,记得 \(10^8 \times 10^8 = 10^{16} > 2^{31}-1\) 会爆 int,开 long long 就好。

于是这个题就变成了一个读入输出优化模板题。这不又回去了

另外,输入输出常数优化也很常用,抢最优解和骗分时都可以用上。

1 cin/cout 版本

操作

ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

第一行开启后就不再兼容 stdio 了,也就是说,只能用 cincout,不能用 scanfprintf 等 C 语言留下的东西了。

原理

?> 为什么要保留 C 语言的东西?
因为要保证 C++ 语言对 C 语言的兼容性(注:C 语言相当于 C++ 它爹),所以 C++ 将这两个的读入输出流绑到了一起。这样是线程安全的。但是,兼容性、安全性、效率经常会有矛盾,这行代码就是从兼容性到取效率。所以,此行代码一旦执行,cincoutprintfscanf 不得再同时使用,但是 cinprintfcoutscanf 还是能同时用的。

剩下两行是在解除绑定?什么绑定?ostreamistream(是的,他俩拼起来就是 iostream)。cin.tie() 默认绑定的是 &std::cout,这不没了?所以这行代码就是在解除绑定。

!> 注意:解除绑定后一定要及时 flush 刷新缓冲区(或者是用 endl,它就是 \nflush 拼出来的)才能保证先输出 cout 里的再输入。

打包

当然,一般 cin/cout 优化这 3 行会一起使用,因此可以将其打包成一个宏。

#define IOS                      \
	ios::sync_with_stdio(false); \  // 这里的'\'表示换行
	cin.tie(nullptr);            \
	cout.tie(nullptr);

main 函数直接调用 IOS 就相当于 3 行一起上了。但一般还是跑不过 printf/scanf。

另外,不要用 endl,它会刷新缓冲区,因此比 \n 慢多了。

不过这里没啥用(字符串时有大用),这题该 TLE 还是 TLE。

2 基础版

?> Tips:这货搞 __int128 时也有用。

输入

首先肯定要处理一下符号问题。“+” 一般直接省略,不管它。“-” 不可省略,特判一下。

for (; !isdigit(ch); ch = getchar())  // isdigit 函数可以判断传过去的形参是否是数字
		if (ch == '-') fl = -1;

这样就可以略过空格和 “+”,直接读入 “-”。

后面的正负就都一样啦。那该咋办?

还记得进制转换吗?转成 10 进制的时候是不是这么写的:

int ans = 0;
for (int i = 0;i < len;++i) ans *= 10, ans += s[i] - '0';

一样的嘛!把 s[i] 换成刚才的 ch,把 for 循环换成 while 即可。总结一下就是这样:

inline int read() {  // inline 是定义内联函数的,可以省去调用函数的常数,具体是把这个函数里的东西干进调用的地方,因此一般只用于小函数
	int sum = 0, fl = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar)
		if (ch == '-') fl = -1; 
	for (; isdigit(ch); ch = getchar) sum = sum * 10 + ch - '0';
	return sum * fl;
}

输出

输出是一样的,直接数位分解。把 getchar 替换为 putchar 即可。

inline void write(int x) {
	if (x < 0) {
		putchar('-'), write<int>(-x);
		return;  // 不加就会反复输出负号
	}
	static T sta[110];
	int top = 0;
	do { sta[top++] = x % 10, x /= 10; } while (x);
	while (top) putchar(sta[--top] + 48);
}

3 变通用

但是!我们还可以让它变得更加通用!

为什么说上面的不通用?因为变量都是 int 的,long longshort 啥的全废了。

好在 C++ 提供了 template 这个好东西,中文名叫模板。可以根据给定的类型和一个模板函数、类生成一堆函数或类。

想想你的 vectorstack,是不是写的 vector<int><> 这个东西里面就是要传过去的类型。

于是可以用 template 改写上面的两个函数,现在是这样的:

template <typename T>

inline T read() {
	T sum = 0, fl = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar)
		if (ch == '-') fl = -1;
	for (; isdigit(ch); ch = getchar) sum = sum * 10 + ch - '0';
	return sum * fl;
}

template <typename T>

inline void write(T x) {
	if (x < 0) {
		putchar('-'), write<T>(-x);
		return;
	}
	static T sta[110];
	int top = 0;
	do { sta[top++] = x % 10, x /= 10; } while (x);
	while (top) putchar(sta[--top] + 48);
}

使用如下:

n = read<int>();  // read 没有参数,必须要加
write<int>(n);  // 注:write 时 “<int>” 写不写一样,不写会自动识别参数类型
w = read<long long>();
write<long long>(w);

一般这么整够了,但是这题还是会 TLE 一个点,于是,我们需要更快的方案!

4 中端版

getchar_unlocked

这是个啥?

官方管它叫“getchar 的非线程安全版本”。鉴于 OI 题目都是单线程程序(多线程那还了得?),安全性不影响。但以后写能赚 $ 的程序时记得注意不要使用。速度比 getchar 还要快,果然安全性与性能不可兼得。同理还有 putchar_unlocked,替换过后就可以 AC 了~。

!> 这个函数并不通用,实测 Windows 不可用,请在 Linux (如某谷评测姬)使用~

5 高端版

freadfwrite

原理

它们和 getcharputchar 原理是一样的,都是拿 1B 东西出来。不同的是,getcharputchar 是从硬盘(文件)中读,这俩是从内存中直接拿。鉴于内存速度快的太吓人,因而这两个也要比 getchar 快。

使用

所以我们可以重新定义一个 getcharputchar

char buf[1 << 20], *p1, *p2;
#define gc() \
(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++) \

char pbuf[1 << 20], *pp = pbuf;
void push(const char &c) {
  if (pp - pbuf == 1 << 20) fwrite(pbuf, 1, 1 << 20, stdout), pp = pbuf;
  *pp++ = c;
}

这样就可以把毒瘤造的毒瘤题的毒瘤数据过掉了~

!> 这种方法速度最快,但相对麻烦,可读性较差,建议不要使用。


参考资料:
OI-Wiki

标签:10,ch,putchar,P10815,int,题解,cout,读入,getchar
From: https://www.cnblogs.com/leo2011/p/18330823

相关文章

  • 奇怪的汉诺塔 - 题解
    奇怪的汉诺塔时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述汉诺塔问题,条件如下:这里有\(A\)、\(B\)、\(C\)和\(D\)四座塔。这里有\(n\)个圆盘,\(n\)的数量是恒定的。每个圆盘的尺寸都不相同。所有的圆盘在开始时都堆叠在塔\(A\)......
  • 昆虫繁殖 - 题解
    昆虫繁殖时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。每对成虫过\(x\)个月产\(y\)对卵,每对卵要过两个月长成成虫。假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后......
  • 【入门】汉诺塔的移动次数 - 题解
    【入门】汉诺塔的移动次数时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++128MB,其他语言256MB描述汉诺塔的问题大家都已经很熟悉了,有三个柱子,每个柱子上有一些大小不一的金片,要把金片从\(A\)柱移动到\(C\)柱,可以借助\(B\)柱,请问\(n\)个金片的情况下,需要最少移......
  • 【基础】递归问题—汉诺塔 - 题解
    【基础】递归问题—汉诺塔时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++127MB,其他语言254MB描述汉诺塔(又称河内塔)问题是印度的一个古老的传说。开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒,第一根上面套着\(64\)个圆的金片,最大的一个在底下,其余一个比一个小......
  • 放苹果 - 题解
    时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述把\(M\)个同样的苹果放在\(N\)个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)\(5,1,1\)和\(1,5,1\)是同一种分法。输入描述第一行是测试数据的数目\(t\)(\(0≤t≤20\))。......
  • 【入门】统计每个月兔子的总数 - 题解
    【入门】统计每个月兔子的总数时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++16MB,其他语言32MB描述有一对兔子,从出生后第3个月起每个月都生一对兔子,一对小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第\(n\)个月(\(n<=50\))的兔子总数为多少对?输入描述......
  • 排序 - 题解
    排序时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++64MB,其他语言128MB描述给定你一个长度为\(n\)的整数数列。请你使用任意排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。数据范围\(1≤n≤100000\)禁止使用sort函数输入描述输入共两......
  • 逆序对的数量 - 题解
    逆序对的数量时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++64MB,其他语言128MB描述给定一个长度为\(n\)的整数数列,请你计算数列中的逆序对的数量。逆序对的定义如下:对于数列的第\(i\)个和第\(j\)个元素,如果满足\(i<j\)且\(a[i]>a[j]\),则其为一个逆序对;否则......
  • 求第k小的数 - 题解
    求第k小的数时间限制:C/C++1500MS,其他语言3000MS内存限制:C/C++256MB,其他语言512MB描述输入\(n\)个数字,输出这些数字的第\(k\)小的数。最小的数是第\(0\)小。输入描述第一行包含两个整数\(n(1≤n≤5000000)\)和\(k(0≤k<n)\)。输出描述1个整数(所有整数均在......
  • 最大子段和 - 题解
    最大子段和时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++128MB,其他语言256MB描述给出一个长度为\(n\)的序列\(a\),选出其中连续且非空的一段使得这段和最大。输入描述第一行是一个整数,表示序列的长度\(n\)。第二行有\(n\)个整数,第\(i\)个整数表示序列的第......