Jetpack 之 Ink API初探

前言

近期看到谷歌官方推文有一篇关于Jetpack Ink API的文章,随即进行了了解和研究,该SDK主要就是低延时的手写绘制,比如通过手指或者触控笔在安卓设备上面进行笔记记录或者在安卓设备上面进行素描之类类似于纸张上面的操作。当然了可能现在市场上已经存在了此类APP,或者此类的SDK,具体实现方式可能不一,有通过Canvas实现的或者OpenGL实现的,但如果没有系统层面支持的话,一般书写延迟都会相对较高,特别是屏幕越大,越明显,但博主对该SDK书写进行了验证,确实非常丝滑。

下面看下官方是如何描述的:

在这里插入图片描述

总结下可能就以下几点:

  • 1、笔记书写,这个就不用多说了,主要功能。

  • 2、低延迟,功能特色,同纸张上面书写一样流畅,号称延迟降低到了4ms,这一点其实博主是抱怀疑态度的,熟知安卓系统显示的同学可能都知道,要实现这一点,应该难度非常大,况且这个SDK还是通用的,当然也可能是实验性的特定设备吧,并不是所有设备都能达到4ms。

  • 3、对于书写实现简单,但是几何图形方面可能会相对会困难一些。当然这一点博主是根据官方字面意思看到的,本篇主要是对书写方面进行了实现,实际博主还未进行几何图形方面的验证,但后续会持续进行该SDK探索,敬请期待。

添加Ink API依赖

libs.version.toml文件:

[versions]
ink = "1.0.0-alpha01"
input = "1.0.0-beta05"[libraries]
ink-authoring = {group = "androidx.ink", name = "ink-authoring", version.ref = "ink"}
ink-brush = {group = "androidx.ink", name = "ink-brush", version.ref = "ink"}
ink-geometry = {group = "androidx.ink", name = "ink-geometry", version.ref = "ink"}
ink-nativeloader = {group = "androidx.ink", name = "ink-nativeloader", version.ref = "ink"}
ink-rendering = {group = "androidx.ink", name = "ink-rendering", version.ref = "ink"}
ink-strokes = {group = "androidx.ink", name = "ink-strokes", version.ref = "ink"}
input-motionprediction =  {group = "androidx.input", name = "input-motionprediction", version.ref = "input"}

app下的build.gradle中添加:

dependencies {//...implementation(libs.ink.authoring)implementation(libs.ink.brush)implementation(libs.ink.geometry)implementation(libs.ink.nativeloader)implementation(libs.ink.rendering)implementation(libs.ink.strokes)implementation(libs.input.motionprediction)
}

代码实现

通过Ink API实现书写的时,我们需要用到SDK中的InProgressStrokesView控件,如下先将其添加到布局:

    <androidx.ink.authoring.InProgressStrokesViewandroid:id="@+id/inProgressStrokesView"android:layout_width="match_parent"android:layout_height="match_parent"/>

为了可持续验证与后续方便使用,这里我对InProgressStrokesView功能进行了封装,新建一个名为InProgressStrokesViewWrapper的类:

class InProgressStrokesViewWrapper(private var inProgressStrokesView: InProgressStrokesView):OnTouchListener{//预测器,加速过程中会用到点的预测private val predictor:MotionEventPredictor = MotionEventPredictor.newInstance(inProgressStrokesView)//这个东西可以理解为笔形吧private val defaultBrush = Brush.createWithColorIntArgb(family = StockBrushes.pressurePenLatest,colorIntArgb = Color.Black.toArgb(),size = 10F,epsilon = 0.1F)init {//添加触控监听器inProgressStrokesView.setOnTouchListener(this)}
}

这个类实际上可以看做是对InProgressStrokesView的代理,在该类中首先创建看了一个触控点的预测器,这个预测器主要是为了触控加速使用,使触控点更加跟手,接着创建了一个书写的笔形defaultBrush,InProgressStrokesView在绘制时将会通过这个笔形进行书写形状的绘制,让我们继续完善这个类,实现OnTouchListener接口:

    override fun onTouch(view: View, event: MotionEvent): Boolean {//调试过程中发现如果点的位置和事件事件一致的话会崩溃,常见于某些设备up时上报的点与最后一个move的点,这块简单加了一个逻辑可能并不是很合适。if(lastEventPoint.x == event.x&&lastEventPoint.y==event.y){event.setLocation(event.x+1,event.y+1)}lastEventPoint.set(event.x,event.y)//预测器记录当前的触控事件predictor.record(event)//预测触控事件val predictedEvent = predictor.predict()try {when (event.actionMasked) {MotionEvent.ACTION_DOWN ->  touchActionDown(view,event)MotionEvent.ACTION_MOVE -> touchActionMove(view,event,predictedEvent)MotionEvent.ACTION_UP -> touchActionUp(view,event)MotionEvent.ACTION_CANCEL -> touchActionCancel(view,event)else -> false}}finally {predictedEvent?.recycle()}return true}

因为调试的过程中发现某些设备上面绘制时存在崩溃,定位下来是因为前后两个点的坐标和时间完全一致导致的,因为崩溃点在native暂时不知道怎么看源码,可能是SDK内有什么保护,所以为什么这样会导致崩溃,未知!所以这里简单的加了个保护,可能并不是很合适,但暂且如此吧,主要也是为了看效果。

接着是预测器记录当前的触控,基于当前的触控进行预测并返回预测结果。

when代码块就是通过触控事件进行绘制逻辑的代码了,在看实际实现之前,我们先来了解下InProgressStrokesView的手写绘制流程。

触控事件InProgressStrokesView 方法描述
ACTION_DOWNstartStroke()开始笔画渲染
ACTION_MOVEaddToStroke()继续渲染笔画
ACTION_UPfinishStroke()完成笔画渲染
ACTION_CANCEL or FLAG_CANCELEDcancelStroke()取消笔画渲染

通过上面这个表格,大家应该能直观可以的看出来InProgressStrokesView的绘制流程,这里就不再过多赘述,让我我们继续。

    //触控按下private fun touchActionDown(view: View, event: MotionEvent){// 这个号称无缓冲调度模式,看着好像会对触控有所加速view.requestUnbufferedDispatch(event)//Demo仅支持单笔绘制(看官方接口设计应该是可以支持多点书写),因此这里记录了第一个触发down事件的触控点的Idval pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)currentPointerId = pointerId//启动绘制currentStrokeId = inProgressStrokesView.startStroke(event = event,           //当前触控事件pointerId = pointerId,   //触控点Idbrush = defaultBrush     //书写笔形)}

触控按下的时候首先给当前View设置了无缓冲调度模式,看API描述是强烈建议这么做,应该是会对触控有所加速吧。

然后保存了当前触控点的Id,后续只会针对该触控点进行笔画书写,在最后调用了inProgressStrokesView.startStroke告诉InProgressStrokesView可以开始笔画绘制了。

    //触控移动private fun touchActionMove(view: View, event: MotionEvent,predictedEvent:MotionEvent?){val pointerId = currentPointerIdval strokeId = checkNotNull(currentStrokeId)for (pointerIndex in 0 until event.pointerCount) {if (event.getPointerId(pointerIndex) != pointerId) continue//继续绘制inProgressStrokesView.addToStroke(event,              //触控事件pointerId,          //触控点IdstrokeId,           //启动绘制返回的strokeIdpredictedEvent      //预测后的触控事件)}}

在收到触控移动事件时,只针对按下事件时记录的触控id进行了笔画的绘制处理,看API应该是可以支持多点同时绘制,但是我们主要是进行初次运行验证,所以就没有做成多点,先有0.5然后再有1吧。

    //触控抬起private fun touchActionUp(view: View, event: MotionEvent){val pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)check(pointerId == currentPointerId)val currentStrokeId = checkNotNull(currentStrokeId)inProgressStrokesView.finishStroke(event,pointerId,currentStrokeId)}

在触控抬起事件时处理与移动事件类似,都是先校验触控点是否是我们需要进行处理的触控点,如果时这里会调用finishStroke结束笔画绘制。

  //触控取消private fun touchActionCancel(view: View, event: MotionEvent){val pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)check(pointerId == currentPointerId)val currentStrokeId = checkNotNull(currentStrokeId)//触控取消后,也要执行书写的取消绘制inProgressStrokesView.cancelStroke(currentStrokeId, event)}

同样的逻辑,触控取消也需要判断是那个触控点取消了,如果是我们当前绘制的,就直接cancelStroke(),逻辑都不难,大家看下都能看懂应该。

至此笔画绘制流程就已经添加结束了,但是好像少了点什么,对了,我们需要添加一个清空绘制内容的函数,还有一个用户保存绘制内容的函数

让我们先来看清除绘制如何实现。

    //清空书写内容fun clearStrokes(){//清空的话,可以清空所有,也可以清空一部分,可以自行选择,这里是清空所有val finishedStrokes = mutableMapOf<InProgressStrokeId, Stroke>().apply { putAll(inProgressStrokesView.getFinishedStrokes()) }inProgressStrokesView.removeFinishedStrokes(finishedStrokes.keys)}

这个实现很简单,首先是通过inProgressStrokesView.getFinishedStrokes()获取到当前已经完成的所有的笔画,然后将需要删除的笔画,通过removeFinishedStrokes函数传给inProgressStrokesView进行删除即可,嗯…饶了一圈的感觉,不过通过这个删除笔画的函数应该不难看出,是可以选择性的进行删除的(比如实现撤销),不过这里做的是全部清空。

接下来看如何保存我们绘制的内容。

    //将书写内容保存为bitmapfun saveAsBitmap(): Bitmap {//创建一个bitmap用于保存绘制内容var bitmap = Bitmap.createBitmap(inProgressStrokesView.width,inProgressStrokesView.height,Bitmap.Config.ARGB_8888)var canvas = Canvas(bitmap)val canvasTransform = Matrix()//保存时需要通过Ink API的渲染器进行操作,这个是必须的val canvasStrokeRenderer = CanvasStrokeRenderer.create()//拿到所有的已经完成的笔画val finishedStrokes = mutableMapOf<InProgressStrokeId, Stroke>().apply { putAll(inProgressStrokesView.getFinishedStrokes()) }finishedStrokes.forEach { (_, stroke) ->canvasStrokeRenderer.draw(canvas,stroke,canvasTransform)}return bitmap}

这个函数首先创建了一个用于保存所有笔画的bitmap,接着基于该bitmap创建了一个Canvas,这个Canvas的绘图操作全部被CanvasStrokeRenderer所代理,别问为什么,问就是Ink API设计如此,我本来想看下它内部实现,但是这里面有点绕,得花点时间理理,后续有机会再补充,这里额外说下canvasTransform这个是Matrix,如果有这个的话,那么说明,理论上inProgressStrokesView的画图区域可以无限大。

看到这里我们InProgressStrokesViewWrapper的代码就已经全部完成了,接下来让我们看看怎么使用,花了这么长时间进行封装了,使用那肯定是相当简单的,简单的几行代码就可以搞定:

 private lateinit var inkWrapper:InProgressStrokesViewWrapperinkWrapper = InProgressStrokesViewWrapper(findViewById(R.id.inProgressStrokesView))

接着通过inkWrapper对象就可以进行清空绘制内容:

 inkWrapper.clearStrokes()   

或者保存为bitmap:

var bitmap = inkWrapper.saveAsBitmap()

至此我们Jetpack Ink API初探在这里就算是圆满结束了,感谢大家观看,本次结束,意味着下次的开始,后续我将继续探索Ink API的使用,以期能够发现其核心低延迟的奥秘。

另附上InProgressStrokesViewWrapper完整源码:

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.PointF
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import androidx.ink.authoring.InProgressStrokeId
import androidx.ink.authoring.InProgressStrokesView
import androidx.ink.brush.Brush
import androidx.ink.brush.StockBrushes
import androidx.ink.brush.color.Color
import androidx.ink.brush.color.toArgb
import androidx.ink.rendering.android.canvas.CanvasStrokeRenderer
import androidx.ink.strokes.Stroke
import androidx.input.motionprediction.MotionEventPredictorclass InProgressStrokesViewWrapper(private var inProgressStrokesView: InProgressStrokesView):OnTouchListener{private var currentPointerId = -1private var currentStrokeId: InProgressStrokeId? = nullprivate var lastEventPoint = PointF()//预测器,加速过程中会用到点的预测private val predictor:MotionEventPredictor = MotionEventPredictor.newInstance(inProgressStrokesView)//这个东西可以理解为笔形吧private val defaultBrush = Brush.createWithColorIntArgb(family = StockBrushes.pressurePenLatest,colorIntArgb = Color.Black.toArgb(),size = 10F,epsilon = 0.1F)init {//添加触控监听器inProgressStrokesView.setOnTouchListener(this)}//清空书写内容fun clearStrokes(){//清空的话,可以清空所有,也可以清空一部分,可以自行选择,这里是清空所有val finishedStrokes = mutableMapOf<InProgressStrokeId, Stroke>().apply { putAll(inProgressStrokesView.getFinishedStrokes()) }inProgressStrokesView.removeFinishedStrokes(finishedStrokes.keys)}//将书写内容保存为bitmapfun saveAsBitmap(): Bitmap {//创建一个bitmap用于保存绘制内容var bitmap = Bitmap.createBitmap(inProgressStrokesView.width,inProgressStrokesView.height,Bitmap.Config.ARGB_8888)var canvas = Canvas(bitmap)val canvasTransform = Matrix()//保存时需要通过Ink API的渲染器进行操作,这个是必须的val canvasStrokeRenderer = CanvasStrokeRenderer.create()//拿到所有的已经完成的笔画val finishedStrokes = mutableMapOf<InProgressStrokeId, Stroke>().apply { putAll(inProgressStrokesView.getFinishedStrokes()) }finishedStrokes.forEach { (_, stroke) ->canvasStrokeRenderer.draw(canvas,stroke,canvasTransform)}return bitmap}override fun onTouch(view: View, event: MotionEvent): Boolean {//调试过程中发现如果点的位置和事件事件一致的话会崩溃,常见于某些设备up时上报的点与最后一个move的点,这块简单加了一个逻辑可能并不是很合适。if(lastEventPoint.x == event.x&&lastEventPoint.y==event.y){event.setLocation(event.x+1,event.y+1)}lastEventPoint.set(event.x,event.y)//预测器记录当前的触控事件predictor.record(event)//预测触控事件val predictedEvent = predictor.predict()try {when (event.actionMasked) {MotionEvent.ACTION_DOWN ->  touchActionDown(view,event)MotionEvent.ACTION_MOVE -> touchActionMove(view,event,predictedEvent)MotionEvent.ACTION_UP -> touchActionUp(view,event)MotionEvent.ACTION_CANCEL -> touchActionCancel(view,event)else -> false}}finally {predictedEvent?.recycle()}return true}//触控按下private fun touchActionDown(view: View, event: MotionEvent){// 这个号称无缓冲调度模式,看着好像会对触控有所加速view.requestUnbufferedDispatch(event)//Demo仅支持单笔绘制(看官方接口设计应该是可以支持多点书写),因此这里记录了第一个触发down事件的触控点的Idval pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)currentPointerId = pointerId//启动绘制currentStrokeId = inProgressStrokesView.startStroke(event = event,           //当前触控事件pointerId = pointerId,   //触控点Idbrush = defaultBrush     //书写笔形)}//触控移动private fun touchActionMove(view: View, event: MotionEvent,predictedEvent:MotionEvent?){val pointerId = currentPointerIdval strokeId = checkNotNull(currentStrokeId)for (pointerIndex in 0 until event.pointerCount) {if (event.getPointerId(pointerIndex) != pointerId) continue//继续绘制inProgressStrokesView.addToStroke(event,              //触控事件pointerId,          //触控点IdstrokeId,           //启动绘制返回的strokeIdpredictedEvent      //预测后的触控事件)}}//触控抬起private fun touchActionUp(view: View, event: MotionEvent){val pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)check(pointerId == currentPointerId)val currentStrokeId = checkNotNull(currentStrokeId)inProgressStrokesView.finishStroke(event,pointerId,currentStrokeId)}//触控取消private fun touchActionCancel(view: View, event: MotionEvent){val pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)check(pointerId == currentPointerId)val currentStrokeId = checkNotNull(currentStrokeId)//触控取消后,也要执行书写的取消绘制inProgressStrokesView.cancelStroke(currentStrokeId, event)}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/471039.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot参数注解

SpringBoot参数注解 常用参数注解 RequestParmPathVariableRequestHeaderCookieValueRequestbody 1.请求参数注解&#xff1a;RequestParm 用途&#xff1a;用于将方法参数绑定到URI查询参数或者表单参数。他可以帮助我们或者HTTP请求中的参数值并将其作为方法的参数进行处…

HarmonyOS的@State装饰器的底层实现

HarmonyOS的State装饰器的底层实现 序言准备工作实现State装饰器 序言 ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript&#xff08;简称TS&#xff09;基本语法风格的基础上&#xff0c;进一步通过规范强化静态检查和分析&#xff0c;使得在程序运行之前的开发期能检测更…

C语言 | Leetcode C语言题解之第557题反转字符串中的单词III

题目&#xff1a; 题解&#xff1a; char* reverseWords(char* s) {int length strlen(s);char* ret (char*)malloc(sizeof(char) * (length 1));ret[length] 0;int i 0;while (i < length) {int start i;while (i < length && s[i] ! ) {i;}for (int p …

响应式网页设计--html

一&#xff0c;HTML 文档的基本结构 一个典型的 HTML 文档包含了几个主要部分&#xff0c;基本结构如下(本文以下出现的所有代码都可以套入下面示例进行测试)&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8&q…

Linux git-bash配置

参考资料 命令提示符Windows下的Git Bash配置&#xff0c;提升你的终端操作体验WindowsTerminal添加git-bash 目录 一. git-bash配置1.1 解决中文乱码1.2 修改命令提示符 二. WindowsTerminal配置git-bash2.1 添加git-bash到WindowsTerminal2.2 解决删除时窗口闪烁问题 三. VS…

【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)

关键词&#xff1a;一多、响应式、媒体查询、栅格布局、断点、UI 随着设备形态的逐渐增多&#xff0c;应用界面适配也面临着很大问题&#xff0c;在以往的安卓应用开发过程中&#xff0c;往往需要重新开发一套适用于大屏展示的应用&#xff0c;耗时又耗力&#xff0c;而鸿蒙提供…

向日葵软件Windows系统连接苹果系统(MacOS)的无反应问题解决办法

前言 向日葵软件最近开始收费了的&#xff0c;打算收割我们。这也是没有办法的事情&#xff0c;毕竟他们的程序员也是需要吃饭的&#xff0c;我也表示理解。 所以&#xff0c;我在连接了几次发现反应很迟钝后&#xff0c;果断的买了158元的包年会员。 但是&#xff0c;在买了会…

neo4j desktop基本入门

下载安装不在赘述&#xff0c;本文只记述一些neo4j的基本入门操作 连接本地neo4j数据库 1. 点击ADD添加连接 端口一般是7687 账户名和密码忘记了&#xff0c;可以通过neo4j web&#xff08;默认为neo4jneo4j://localhost:7687/neo4j - Neo4j Browser&#xff09;重置密码 AL…

编写红绿起爆线指标(附带源码下载)

编写需求&#xff1a; 想问问有没有能标注行情起爆点的指标。 效果展示&#xff1a; 红线上&#xff0c;出现绿柱转红柱做多。 蓝线下&#xff0c;出现红柱转绿柱做空。 源码展示&#xff08;部分源码&#xff0c;完整源码需下载源码文件&#xff09;&#xff1a; IsMainIn…

ubuntu20.04 解决Pytorch默认安装CPU版本的问题

ubuntu20.04 解决Pytorch默认安装CPU版本的问题 在使用Anaconda安装支持CUDA的PyTorch版本时&#xff0c;遇到只能安装CPU版本的PyTorch是一个常见问题。这通常由于Anaconda环境配置、镜像源设置不当或版本匹配问题导致。以下是详尽的解决方案和步骤&#xff0c;以确保能够正确…

C++《继承》

在之前学习学习C类和对象时我们就初步了解到了C当中有三大特性&#xff0c;分别是封装、继承、多态&#xff0c;通过之前的学习我们已经了解了C的封装特性&#xff0c;那么接下来我们将继续学习另外的两大特性&#xff0c;在此将分为两个章节来分别讲解继承和多态。本篇就先来学…

数字孪生在智慧能源项目中的关键作用,你了解多少?

随着能源行业不断向智能化、数字化转型&#xff0c;数字孪生技术在智慧能源项目中扮演的角色愈发重要。数字孪生不仅带来了前所未有的资源优化和成本节约方式&#xff0c;还为整个能源系统的可持续运营奠定了坚实基础。那么&#xff0c;为什么数字孪生技术在智慧能源项目中如此…

Window下PHP安装最新sg11(php5.3-php8.3)

链接: https://pan.baidu.com/s/10yyqTJdwH_oQJnQtWcwIeA 提取码: qz8y 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 (链接失效联系L88467872) 1.下载后解压文件&#xff0c;将对应版本的ixed.xx.win文件放进php对应的ext目录下&#xff0c;如图所示 2.修改ph…

Postman上传图片如何处理

打开Postman&#xff0c;创建一个新的请求 URL: http://90.104.232.49:80/dev-api/appcommon/upload 如果有解密进入上传就在请求头添加 点击“Body”选项卡。 选择“form-data”类型。 在“KEY”列中输入文件字段的名称&#xff0c;例如file。 在“VALUE”列中&#xff0…

陪诊问诊APP开发实战:基于互联网医院系统源码的搭建详解

时下&#xff0c;开发一款功能全面、用户体验良好的陪诊问诊APP成为了医疗行业的一大热点。本文将结合互联网医院系统源码&#xff0c;详细解析陪诊问诊APP的开发过程&#xff0c;为开发者提供实用的开发方案与技术指导。 一、陪诊问诊APP的背景与功能需求 陪诊问诊APP核心目…

Leecode热题100-35.搜索插入位置

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入:…

Axure设计之文本编辑器制作教程

文本编辑器是一个功能强大的工具&#xff0c;允许用户在图形界面中创建和编辑文本的格式和布局&#xff0c;如字体样式、大小、颜色、对齐方式等&#xff0c;在Web端实际项目中&#xff0c;文本编辑器的使用非常频繁。以下是在Axure中模拟web端富文本编辑器&#xff0c;来制作文…

【MySQL 保姆级教学】事务的隔离级别(详细)--下(13)

事务的隔离级别 1. 如何理解事务的隔离性2. 事务隔离级别的分类3. 查看和设置事务隔离级别3.1 全局和会话隔离级别3.2 查看和设置隔离级别 4. 事务隔离级别的演示4.1 读未提交&#xff08;Read Uncommitted&#xff09;4.2 读已提交&#xff08;Read Committed&#xff09;4.3 …

大厂的 404 页面都长啥样?看看你都见过吗~~~

当我们浏览网页时&#xff0c;不小心走错路径或打开一个已被移除的页面时&#xff0c;常会遇到“404页面”。这时&#xff0c;普通网站往往只会显示冷冰冰的“404 Not Found”&#xff0c;但大厂们却能把404页面玩出花来。国内互联网大厂的404页面不仅独特&#xff0c;而且设计…

acwing算法基础02一高精度,前缀和,差分

#include <iostream> #include <vector> using namespace std;const int N 1e6 10; //模板 CABvector<int> add(vector<int> &A,vector <int> &B) {vector<int> C;int t 0; // 用来保存每位的和&#xff08;包括进位&#xff…