草庐IT

详解 HttpServletResponse

编程小菜鸟~ 2023-04-12 原文

详解 HttpServletResponse

前言

Servlet 中的 doXXX 方法的目的就是根据请求计算得到响应, 然后把响应的数据设置到HttpServletResponse 对象中,
然后 Tomcat 就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式, 转成一个字符串, 并通过Socket 写回给浏览器;

核心方法

方法描述
void setStatus(int sc)设置响应状态码
void setHeader(String name,String value)设置一个带有给定的名称和值的Header,如果name已经存在,则覆盖旧的值
void addHeader(int sc)设置一个带有给定的名称和值的Header,如果name存在,不会覆盖旧的值,并列添加新的值
void setContentType(String type)设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码(MIME 字符集)
void sendRedirect(String location)使用指定的重定向位置 URL 发送临时重定向响应到客户端
PrintWriter getWriter()用于往 body 中写入文本格式数据
OutputStream getOutStream()用于往 body 中写入二进制格式数据

需要注意的是:

  • 响应对象是服务器要返回给浏览器的内容, 这里的重要信息都是程序猿设置的, 因此上面的方法都是 "写" 方法
  • 对于状态码/响应头的设置要放到 getWriter /getOutputStream 之前, 否则可能设置失效;

代码示例

1.设置响应状态码

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h3>设置状态码</h3>
    <input type="text" id="status">
    <br>
    <button onclick="setStatus()">提交</button>
</body>
<script>
    function setStatus(){
        //js中发请求:(1)ajax (2)直接修改地址栏URL
        let status = document.querySelector("#status");
        //后端将文本框输入的值作为响应状态码
        window.location.href= "response?status="+status.value;
    }
</script>
</html>

后端代码

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/response")
public class ResponseStudyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //获取请求发送的queryString数据:status=xxx
        String status = req.getParameter("status");
        resp.setStatus(Integer.parseInt(status));
        resp.getWriter().write("响应状态码设置成功");

    }
}

启动Tomcat 后,页面如下所示:

404 状态码进行提交后,fiddler抓包工具可查看到如下信息:

2.设置响应头

修改前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h3>设置状态码</h3>
    <input type="text" id="status">
    <br>
    <button onclick="setStatus()">提交</button>


    <h3>设置响应头</h3>
    <a href="response">设置</a>
</body>
<script>
    function setStatus(){
        //js中发请求:(1)ajax (2)直接修改地址栏URL
        let status = document.querySelector("#status");
        //后端将文本框输入的值作为响应状态码
        window.location.href= "response?status="+status.value;
    }
</script>
</html>

修改后端代码

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/response")
public class ResponseStudyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求发送的queryString数据:status=xxx
        String status = req.getParameter("status");
        //当请求数据中包含需要设置状态码,才会执行
        if (status != null) {
            resp.setStatus(Integer.parseInt(status));
            resp.getWriter().write("响应状态码设置成功");
        }
        //设置响应头的键值对,键可以是标准的HTTP响应头的键,也可以是自定义的
        //响应状态码是 301,302,307,响应头有location字段,才是重定向
        resp.setHeader("location","http://www.baidu.com" );
        resp.setHeader("username","晓茹");
    }
}

重新启动Tomcat ,刷新页面:

fiddler 抓包结果:

会发现设置了location字段,但并没有跳转,发现响应状态码为200(原因:只有3xx的状态码才会重定向);

注意:

  • 若响应头name键已有,就会覆盖原有的键值对;
  • addHeader ,当name键已存在时,不会覆盖,会添加一个新的;

3.设置响应内容

(1)响应一个网页(简单HTML)

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h3>响应正文为简单的html网页</h3>
    <a href="html?type=1">查看</a>
</body>
</html>

后端代码

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/html")
public class HtmlTypeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //响应html:设置响应的content-type
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter pw = resp.getWriter();

        //获取queryString中,type的值
        String type = req.getParameter("type");
        if("1".equals(type)){
            //返回简单的html
            pw.println("<h3>获取网页成功</h3>");
        }
    }
}

重新启动,刷新页面,点击跳转:

(2)响应一个网页(复杂HTML)

前端代码

 <body>
 <h3>响应正文为复杂的html(动态变化的)</h3>
    <input type="text" id="username" placeholder="输入姓名">
    <br>
    <button onclick="toWelcome()">跳转</button>
</body>
<script>
	    function toWelcome(){
        let username = document.querySelector("#username");
        window.location.href = "html?type=2&username=" +username.value;
    }
</script>

后端代码

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/html")
public class HtmlTypeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //响应html:设置响应的content-type
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter pw = resp.getWriter();

        //获取queryString中,type的值
        String type = req.getParameter("type");
        if("1".equals(type)){
            //返回简单的html
            pw.println("<h3>获取网页成功</h3>");
        }else if("2".equals(type)){
            //返回复杂的html
            //html?type=2&username=xxx
            String username = req.getParameter("username");
            pw.println("<p>");
            pw.println("欢迎你:"+username);
            pw.println("</p>");
        }
    }
}

启动Tomcat ,刷新页面:

输入Java小菜鸟,点击跳转:

当输入另一个姓名(张三)时:

思考:如上Java代码中,写入了许多HTML代码,这样开发好嘛?

答案是肯定不好的,缺点:耦合性太强(两个完全不同的编程语言在一起开发),导致维护性和可扩展性变差!
解决方案:(1)模板技术(也存在一些问题) (2)进一步产生ajax技术;

返回已有的一个网页

1.重定向

前端代码

<body>
   <h3>重定向到request.html</h3>
   <a href="goto?type=1">跳转</a>
 </body>

后端代码:

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/goto")
public class GoToServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重定向到request.html
        //goto?type=xxx
        String type = req.getParameter("type");
        if("1".equals(type)){
            //重定向
            //设置响应状态码:301
            //设置location字段
            resp.setStatus(301);
            resp.setHeader("Location","request.html");
            //以上代码可简化为:
           // resp.sendRedirect("request.html");
        }
    }
}

启动Tomcat,点击跳转,URL地址栏由http://localhost:8080/servlet-study/response.html跳转到http://localhost:8080/servlet-study/resquest.html

结果如下所示:URL地址栏发生了变化

小结

特点URL地址栏会发生变化,会发起两次请求:
原理
第一次返回301、302、307响应状态码及响应头location:网页的地址;
第二次:浏览器自动跳转到location设置的地址;
可以用在登陆成功后跳转到某个页面!

2.转发

前端代码

<body>
    <h3>转发到request.html</h3>
    <a href="goto?type=2">跳转</a>
 </body>  

修改后的后端代码

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/goto")
public class GoToServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重定向到request.html
        //goto?type=xxx
        String type = req.getParameter("type");
        if("1".equals(type)){
            //重定向
            //设置响应状态码:301
            //设置location字段
            resp.setStatus(301);
            resp.setHeader("Location","request.html");
            //以上代码可简化为:
           // resp.sendRedirect("request.html");
        }else if("2".equals(type)){
            //转发
            req.getRequestDispatcher("request.html")
                    .forward(req,resp);
        }
    }
}

启动Tomcat,刷新页面:

会发现URL地址栏并没有发生变化!!!

小结

特点URL地址栏不会发生变化,只有一次请求;
原理:当次请求servlet时,由servlet获取到转发路径的资源,并把这个路径的内容设置到响应正文;

返回一个文件

需要设置一下 content-Typecontent-Length ,然后将文件的二进制数据放在响应正文即可;

渲染展示与下载

示例:图片与音乐

前端代码

<body>
   <h3>获取一个图片(渲染展示)</h3>
    <img src="file?type=photo&show=1">
    <h3>获取一个音乐(渲染展示)</h3>
    <audio src="file?type=music&show=1" controls></audio>

    <h3>获取一个图片(下载)</h3>
    <a href="file?type=photo&show=0">下载</a>
    <h3>获取一个音乐(下载)</h3>
    <a href="file?type=music&show=0">下载</a>

 </body>

后端代码

package response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;

@WebServlet("/file")
public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 获取响应对象的字节输出流
        OutputStream os = resp.getOutputStream();

        // 返回的文件类型:1.图片
        String type = req.getParameter("type");
        // 返回时的操作:1.渲染 2.下载
        String show = req.getParameter("show");

        File file = null;

        // <img src="file?type=photo&show=1">
        if("photo".equals(type)){//返回图片
            if("1".equals(show)) {
                resp.setContentType("image/jpeg");//jpg格式
            }else {
            //文件下载
                resp.setContentType("application/octet-stream");
            }
            file = new File("D:\\servlet-study\\src\\main\\resources\\doge.jpg");

            //<audio src="file?type=music&show=1" controls></audio>
        }else if("music".equals(type)){//返回音乐
            if("1".equals(show)) {
                resp.setContentType("audio/mp3");//mp3格式
            }else {
                resp.setContentType("application/octet-stream");
            }
            file = new File("D:\\servlet-study\\src\\main\\resources\\ 晴天.mp3");
        }


        //返回一个文件类型:Content-Length,body
        byte[] data = Files.readAllBytes(file.toPath());
        resp.setContentLength(data.length);//=setHeader("Content-Length", xxx)
        os.write(data);
    }
}

启动Tomcat ,刷新页面,渲染方式如下:

下载方式如下:


打开文件,修改文件后缀名即可查看图片;

http 菜鸟教程查看 content-type 对应格式

思考:图片、音乐、视频等都是静态文件,直接放在webapp 下,就可以直接访问,还需要servlet来返回嘛?


如果文件总的大小非常大,放在web应用的webapp下就不合适(打包比较费劲),但通过servlet去读取本地其他地方的文件来返回就比较合适;

返回 json 数据

常用于ajax请求,返回一些数据,用于动态的填充网页;

前端代码

 <body>
 <h3>获取ajax响应数据,动态生成网页内容</h3>
    <button onclick="gen()">试试呗</button>
    <div id="content"></div>
</body>
<script>
    function gen(){
        let content = document.querySelector("#content");
        ajax({
            url: "ajax-response",
            method: "get",
            callback: function(status, resp){
                console.log(resp);//resp是一个字符串
                //转换为json对象
                let array = JSON.parse(resp);
                for(json of array){//遍历
                    //每一个json对象,创建一个dom来保存信息
                    let p = document.createElement("p");
                    p.innerHTML = json.from+" 对 "+json.to+" 说:"+json.info;
                    content.appendChild(p);
                }
            }
        });
    }

    function ajax(args){//var ajax = function(){}
        let xhr = new XMLHttpRequest();
        // 设置回调函数
        xhr.onreadystatechange = function(){
            // 4: 客户端接收到响应后回调
            if(xhr.readyState == 4){
                // 回调函数可能需要使用响应的内容,作为传入参数
                args.callback(xhr.status, xhr.responseText);
            }
        }
        xhr.open(args.method, args.url);
        //如果args中,contentType属性有内容,就设置Content-Type请求头
        if(args.contentType){//js中,if判断,除了判断boolean值,还可以判断字符串,对象等,有值就为true
            xhr.setRequestHeader("Content-Type", args.contentType);
        }
        //如果args中,设置了body请求正文,调用send(body)
        if(args.body){
            xhr.send(args.body);
        }else{//如果没有设置,调用send()
            xhr.send();
        }
    }
</script>

后端代码

package response;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/ajax-response")
public class AjaxJsonServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List<Message> messages = new ArrayList<>();
        Message m1 = new Message("花花", "小菜鸟", "你一定可以顺利毕业");
        Message m2 = new Message("小菜鸟", "花花", "我肯定行!!!");
        messages.add(m1);
        messages.add(m2);
        ObjectMapper mapper = new ObjectMapper();
        //把Java对象,转换为一个json字符串。list和数组会转换为[],一个对象{成员变量名: 值}
        String json = mapper.writeValueAsString(messages);
        System.out.println("转换的json字符串:"+json);

        //设置json可以不设置Content-Length,tomcat会设置
        resp.setContentType("application/json; charset=utf-8");
        resp.getWriter().println(json);
    }

    static class Message{
        private String from;//谁
        private String to;//对谁
        private String info;//说了什么


        //构造方法
        public Message(String from, String to, String info) {
            this.from = from;
            this.to = to;
            this.info = info;
        }
        //getter与setter
        public String getFrom() {
            return from;
        }

        public void setFrom(String from) {
            this.from = from;
        }

        public String getTo() {
            return to;
        }

        public void setTo(String to) {
            this.to = to;
        }

        public String getInfo() {
            return info;
        }

        public void setInfo(String info) {
            this.info = info;
        }
    }
}

启动Tomcat,打开网页,查看:

点击试试呗按钮,网页显示:

后端输出:

请求响应流程小结

简单来说就是:

  1. 客户端浏览器发起HTTP请求

客户端浏览器发起请求的方式:
(1)queryString
(2)表单:
(3)form-data
(4)json

如下表所示

  1. 服务端进行处理

服务端处理步骤
(1)通过 HttpServletRequest 先获取到请求数据;

获取方式queryString表单form-datajson
getParameter可以获取简单类型
getPart√(上传的文件)
getInputStream(可以获取任意格式的请求正文的数据)

注意json字符串中的键需要和自定义类型中的成员变量名一致(一般为自定义格式)!

(2)根据请求数据进行校验,业务逻辑处理(如:数据库操作或者根据请求数据的某个字段,执行不同的逻辑,如上面的type=1,返回简单的htmltype=2,返回动态变化的 html);
(3)返回响应的内容;内容有

  • 网页:一般使用模板技术,而不是在servlet拼接动态的html
  • 文件:根据需要;
  • json:前端发送的是ajax请求;

3.返回HTTP响应给客户端

客户端接收并处理服务端HTTP响应

三种方式

(1)网页:渲染方式(浏览器自动完成);
(2)文件:下载或渲染(浏览器自动完成)
(3)jsonJavaScriptajax 代码完成;

有关详解 HttpServletResponse的更多相关文章

  1. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  2. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  3. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  4. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  5. 最强Http缓存策略之强缓存和协商缓存的详解与应用实例 - 2

    HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca

  6. IDEA 2022 创建 Spring Boot 项目详解 - 2

    如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1.  创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1.  创建SpringBoot项目        打开IDEA,选择NewProject创建项目。        填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。        选择springboot版本以及需要的包,此处只选择了springweb。        此处需特别注意,若你使用的是jdk1

  7. 详解Unity中的粒子系统Particle System (二) - 2

    前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif

  8. VMware虚拟机与本地主机进行磁盘共享(详解) - 2

    VMware虚拟机与本地主机进行磁盘共享前提虚拟机版本为Windows10(专业版,不是可能有问题)本地主机为家庭版或学生版(此版本会有问题,但有替代方式)最好是专业版VMware操作1.关闭防火墙,全部关闭。2.打开电脑属性3.点击共享-》高级共享-》权限4.如果没有everyone,就添加权限选择完全控制,然后应用确定。5.打开cmd输入lusrmgr.msc(只有专业版可以打开)如果不是专业版,可以跳过这一步。点击用户-》administrator密码要复杂密码,否则不行。推荐admaiN@1234类型的密码。设置完密码,点击属性,将禁用解开。6.如果虚拟机的windows不是专业版,可

  9. ElasticSearch之 ik分词器详解 - 2

    IK分词器本文分为简介、安装、使用三个角度进行讲解。简介倒排索引众所周知,ES是一个及其强大的搜索引擎,那么它为什么搜索效率极高呢,当然和他的存储方式脱离不了关系,ES采取的是倒排索引,就是反向索引;常见索引结构几乎都是通过key找value,例如Map;倒排索引的优势就是有效利用Value,将多个含有相同Value的值存储至同一位置。分词器为了配合倒排索引,分词器也就诞生了,只有合理的利用Value,才会让倒排索引更加高效,如果一整个Value不进行任何操作直接进行存储,那么Value和key毫无区别。分词器Analyzer通常会对Value进行操作:一、字符过滤,过滤掉html标签;二、分

  10. Educational Codeforces Round 146 (Rated for Div. 2)(B,E详解) - 2

    题外话:抑郁场,开局一小时只出A,死活想不来B,最后因为D题出锅ura才保住可怜的分。但咱本来就写不到DB-LongLegs(数论)本题题解法一学自同样抑郁的知乎作者幽血魅影的题解,有讲解原理。法二来着知乎巨佬cup-pyy(大佬说《不难发现》呜呜)题意三种操作:向上走mmm步向右走mmm步给自己一次走的步数加111,即使得m=m+1m=m+1m=m+1问从(0,0)(0,0)(0,0)走到(a,b)(a,b)(a,b)的最小操作次数,值得注意的是操作三不可逆。解析假设我们最终一步的大小增长到mmm,那么在这个过程中我能以[1,m][1,m][1,m](当步数增长到该数时)之间的任何数字向上或

随机推荐