-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 67.9 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 67.9 KB
1
{"pages":[],"posts":[{"title":"Maven - note 1","text":"【背景】假设我们开发一个项目,或者做一个小demo,如果用···,那么我们就必须将···所依赖的jar包依次找出来,并手动导入。这个过程会想象一下就比较繁琐。 Maven是基于项目对象模型(POM, project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具。 主要作用: 选择获取(特定版本的)jar包。 寻找依赖,下载依赖。 热部署,热编译。(减免重启服务器/重新部署) [TOC] 目录结构 常见的简单结构 123456789┬ /.settings├ /src│ ├ /main│ └ /test├ /target│ └ /class├ .classpath├ .project└ pom.xml 路径/文件 主要用途 /src 主要存放源代码文件,其中一般有两个文件目录; /src/main 放用于运行项目的代码,再下一级的目录就是各种package。 /src/test 放用于测试项目的代码,同时都拥有resource资源文件。 /target 放编译后的代码。 pom.xml 资源文件,确定该项目需要使用哪些jar包的坐标,并通过该坐标在中央仓库进行自动下载。 Tips: 所以 pom.xml 大概是怎么做的? 12345678> <dependencies>> <dependency>> <groupId>org.mybatis</groupId>> <artifactId>mybatis</artifactId>> <version>3.2.5</version>> </dependency>> </dependencies>> 通过三个元素标识一个Maven项目,IDE就能去远程仓库下载了。 安装与配置Win10前提:建议先装好JDK。 访问官网 找到下载 选对文件(win10应该选bin.zip吧?) 查看官方指南 解压到合适的路径 把bin目录的绝对路径,加入系统环境变量PATH 命令行输入mvn -v看看返回的版本信息。 IDEA??? 目录结构12345678910┬ /bin├ /boot├ /conf│ ├ /logging│ ├ settings.xml│ └ toolchains.xml├ /lib├ LICENCE├ NOTICE └ README.txt 路径/文件 主要用途 /bin 放Maven的启动文件,包括两种:一种是直接启动,另一种是通过debug模式下启动。 /boot 放类加载器框架。 /conf 放全局配置文件setting.xml,即为所有仓库都实施的配置。仓库也有私有配置的setting,一般推荐使用私有配置,因为全局配置在Maven的安装目录里。 /lib 放Maven运行需要的各种jar包。 LICENCE Maven的软件使用许可证书 NOTICE Maven包含的第三方软件 README.txt Maven的简单介绍以及安装说明 解决下载——镜像因为众所周知的原因,在国内用Maven一般情况是很难下载到东西的。 现在一般都是在setting.xml的<mirrors>标签里加阿里云的镜像就可以了。 123456<mirror> <id>alimaven</id> <mirrorOf>central</mirrorOf> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/repositories/central/</url></mirror> 解决下载——代理一般是个人希望用工具去下官方仓库的东西,或者有特殊的任务需求才会用把。 能代理的前提是,你有一个可以ping得通repol.maven.org的代理服务器访问权哦。 123456789101112<proxy> <id>optional</id> //代理Id <active>true</active> //是否要激活 <protocol>http</protocol> //采用协议 <username>proxyuser</username> //如果代理需要认证就需要账号密码 <password>proxypass</password> <host>proxy.host.net</host> // ip <port>80</port> //端口 <nonProxyHosts>local.net|some.host.com</nonProxyHosts> //不需要代理的主机ip用户|隔开,例如里面的some.host.com,即所有对这个网站的访问都不需要代理</proxy> 仓库概念 通过pom.xml中的配置,就能够获取到想要的jar包,但是这些jar从哪来?答案就是仓库。 也可以理解成——网盘(?!) 本地仓库Maven会将工程中依赖的构件(Jar包)从远程下载到本机一个目录下管理,每个电脑默认的仓库是在 $user.home/.m2/repository下。 通常人们会修改本地仓库位置,自己创建一个文件夹,在从网上下载一个拥有相对完整jar包集合,都丢到本地仓库中,之后写项目时可以节约下载的时间。 在Maven根目录./conf/setting.xml,修改本地库位置。找到<localRepository>节点,根据旁边的注释依葫芦画瓢即可。 第三方仓库第三方仓库,又称为内部中心仓库,也称为私服。 私服:一般是由公司自己设立的,只为本公司内部共享使用。它既可以作为公司内部构件协作和存档,也可作为公用类库镜像缓存,减少在外部访问和下载的频率。 私服可以使用的是局域网,中央仓库必须使用外网,也就是一般公司都会创建这种第三方仓库,保证项目开发时,项目所需用的jar都从该仓库中拿,每个人的版本就都一样。 注意:连接私服,需要单独配置。如果没有配置私服,默认不使用。 中央仓库Maven内置了远程公用仓库:http://repo1.maven.org/maven2 这个公共仓库是由Maven官方维护,里面有大量的常用类库,并包含了世界上大部分流行的开源项目构件。目前以java为主。 工程依赖的jar包如果本地仓库没有,默认会从中央仓库下载。 你访问不了,就只能依赖其他途径了。(见上文”解决下载“) 参考【长】Maven从入门到精通 【长】maven(一) maven到底是个啥玩意~ 【中】Maven是什么? 【短/附书】Maven的作用到底是什么 【电子书/要注册】Maven实战","link":"/2019/10/06/Maven/Maven-note-1/"},{"title":"Qzone 相册照片链接测试","text":"特别鸣谢阿翔同学让我想到了这个问题。","link":"/2019/08/09/Misc/Qzone-相册照片链接测试/"},{"title":"Github没计绿点的问题","text":"详细问题见:Github为什么没有记录你的Contributions 我的问题就是本地git的账号和github的不一样,在下面这个页面插入即可。https://github.com/settings/emails","link":"/2019/08/09/Misc/Github没计绿点的问题/"},{"title":"Maven - notes 2","text":"一般这种软件都是可以用命令行用的,当然一些现代化的IDE,可以集成一些操作。在本文中,我先对命令行的使用方法做一些了解,然后捣鼓如何在Intellij中使用它。 最不会错不会过时的入门方法,还是去看官网的入门指南(以及民间译文)。本文会在相对忠于官方指南的基础上,补充一些个人觉得不大容易懂的内容。 [TOC] 我觉得常见的项目目录挺重要的,所以再看一下: 123456789┬ /.settings├ /src│ ├ /main│ └ /test├ /target│ └ /class├ .classpath├ .project└ pom.xml 部分术语Artifact 相关的重要常识 In general software terms, an “artifact“ is something produced by the software development process, whether it be software related documentation or an executable file. An artifact is a file, usually a JAR, that gets deployed to a Maven repository. from stackoverflow.com What is a Maven artifact? artifact一般是指软件开发过程中产生的某种文档或可执行文件。在Maven项目中,artifact(有人译作依赖包)一般指某些JAR文件,可以被部署到Maven仓库,运行某个项目/模块。 以下提供一些通俗的理解: artifact是maven中的一个概念,表示某个module的打包。 这又多个module的概念,不懂的就先当他是个web项目,例如war exploded、war、jar、ear等等这种打包的文件,当module有对应的artifacts,即可部署到应用服务器中。 其中war exploded可以理解为展开,不压缩的意思。也就是war、jar等产出物没压缩前的目录结构。建议在开发的时候使用这种artifacts部署,便于修改文件快速生效。 artifact什么意思 Archetypes In short, Archetype is a Maven project templating toolkit. 简单地说,Archetype 是Maven项目的一个模板工件。 可以在Introduction to Archetypes页面找到构建各种项目的模板生成指令。可以节省自己建立文件结构的麻烦。(当然借助IDE插件另当别论) 使用Win10命令行在这里,我想说,网上有很多博客已经过时了。 maven在3.0.5版本之后废弃了create命令,如果要使用maven命令来创建maven项目需要用generate替换create来创建一个maven项目。 本文是在笔者使用Apache Maven 3.6.2版本的时候写的。 打个模板选择一个喜欢的目录,启动命令行输入以下指令就会开始下载一些模板组件, 1mvn archetype:generate 这里的模板也就是指Archetypes,可以在Introduction to Archetypes页面阅读其他选择的说明,这里只是练习一个例程。 下载完毕会让你填写项目的信息,也会作为本项目打包以后的包信息。 属性 介绍 groupId (Maven)项目的唯一标识。Group ID必须满足 Java包名规范 ,这意味着是形如:org.apache.maven,org.apache.commons的格式。Maven不强制此规范,很多遗留的旧项目使用单个单词作为group ID。但是,很难用单个单词作为(新项目的)group ID,并将其提交到Maven中央仓库中。 artifactId 不带版本号的jar的名字。唯一要求是使用小写字母,且没有特殊符号。如果这是一个第三方的jar包,必须使用其被发布的包名,例如:maven,commons-math version 如果要发布,可选择典型的数字和点号组成的版本号(1.0,1.1,1.0.1…)不要使用日期,因为它们通常和SNAPSHOT版本号联系在一起。如果是一个第三方的artifact,你必须使用它提供的版本号,无论这些版本号看起来多么奇怪,比如:2.0,2.0.1,1.3.1 笔者的记录: 12345678910Define value for property 'groupId': www.xxx.zzzDefine value for property 'artifactId': xxxDefine value for property 'version' 1.0-SNAPSHOT: : 0.1Define value for property 'package' www.xxx.zzz: :Confirm properties configuration:groupId: www.xxx.zzzartifactId: xxxversion: 0.1package: www.xxx.zzz Y: : Y 好吧这样写是不好的示例。 笔者第二次认真写的记录: 12345678910111213Choose archetype:...Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 7:Define value for property 'groupId': com.mycompany.appDefine value for property 'artifactId': my-appDefine value for property 'version' 1.0-SNAPSHOT: :Define value for property 'package' com.mycompany.app: :Confirm properties configuration:groupId: com.mycompany.appartifactId: my-appversion: 1.0-SNAPSHOTpackage: com.mycompany.app Y: : 中间空的冒号都是直接按回车,使用默认的。 这一步做完之后就会在本目录找到刚刚拉到的模板项目。 阅读pom.xml1234567891011121314151617181920212223242526<project xmlns=\"...\" xmlns:xsi=\"...\" xsi:schemaLocation=\"...\"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>my-app</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project> 节点 介绍 project 这是Maven pom.xml文件中最顶级的节点。 modelVersion 这个节点指示 POM的对象模型试验什么版本。模型的版本改变非常少但是这是强制性的为了确定使用的稳定性和当Maven开发者认为至为重要的是改变这个模型。 groupld 这个节点指示是创建项目的组织或组的唯一标识符。这个groupId是项目键标识符的其中一个和通常基于完整有资格的组织的领域名字。例子:org.apache.maven.plugins 是设计的一个组对于所有Maven插件。 artifactId 这个节点指示是私有工件本被项目生成的唯一基本名字。这个项目的私有工件通常是一个JAR文件。第二,工件像源码捆也用于这个工件作为他们最终的名字。一个通常的由Maven生成的工件会有表单-.(例如 myapp-1.0.jar)。 packaging 这个节点是用于这个artifact打包类型(例如 JAR,WAR,EAR 等等)。这不仅意味着工件生成的是JAR,WAR,EAR但是指示是一个指定生命周期使用,作为构建进程的一部分。(这个周期是一个话题我们将处理并进一步研究在这个指南里。对于现在,只要记住项目的packaging指示可以作为一部分在自定义构建周期中。)packaging的默认值是JAR 所以你不需为大部分项目指定。 version 这个节点指示着这个工件被创建的版本。Maven有很长的一段路来帮助你管理版本和你经常看的SNAPSHOT指示器在这个版本中,它表示这这个项目是正在开发的状态。我们将讨论快照的使用和他们怎么进一步使用在指南中。 name 这个节点指示这项目显示名字,这是经常使用Maven的生成文件。 url 此元素指示项目的网站可在何处找到。这是经常使用Maven的生成文件。 description 此元素为您的项目提供基本说明。这是经常使用Maven的生成文件。 tips: jar、war、ear都是什么? jar、war、ear、在文件结构上,三者并没有什么不同,它们都采用zip或jar档案文件压缩格式。但是它们的使用目的有所区别: Jar文件(扩展名为. Jar,Java Application Archive)包含Java类的普通库、资源(resources)、辅助文件(auxiliary files)等 War文件(扩展名为.War,Web Application Archive)包含全部Web应用程序。在这种情形下,一个Web应用程序被定义为单独的一组文件、类和资源,用户可以对jar文件进行封装,并把它作为小型服务程序(servlet)来访问。 Ear文件(扩展名为.Ear,Enterprise Application Archive)包含全部企业应用程序。在这种情形下,一个企业应用程序被定义为多个jar文件、资源、类和Web应用程序的集合。 当你的项目已经完成了,那么就打个war包吧,这个时候一个war文件就相当于一个web应用程序;而jar文件就是把类和一些相关的资源封装到一个包中,便于程序中引用。 from blog.csdn.net JAR、WAR、EAR的使用和区别 参考 【长】《Maven官方指南》30分钟入门 | 并发编程网 – ifeve.com 【中】Maven是什么? 【中】Maven artifact版本管理规范,同级引用以及打包 【短/译】Maven项目groupId、artifactId和version命名规范","link":"/2019/10/07/Maven/Maven-notes-2/"},{"title":"Hexo+ICARUS 使用指南与文章模板","text":"小白入门 文件头以这篇文章为例,可以知道这每个文件开头面要写这些东西,设置了文件的各种信息。 最好是从bash里hexo new “xxx” 在名字之前可以带一个模板的名字(不用引号),也可以预先准备一些写好的东西,挺方便的。 12345678910---title: Hexo+ICARUS 使用指南与文章模板date: 2019-08-05 22:05:36tags: //无顺序,单一层级- blogsite- postguidetoc: true //启用章内索引categories: //有顺序,体现分类层级- misc--- 插入图片简单的做法是安装插件, 1npm install hexo-asset-image --save Make sure post_asset_folder: true in your _config.yml. Just use  to insert logo.jpg. 但是 截止目前,这个插件的作者好像没有更新npm的包,所以github上的代码是最新的,我是直接下载放入.\\node_modules路径里,才真正生效。 折叠文章你会发现本样式中的文章默认在主页是不折叠的,需要在文章里写: 1<!--more--> 这样的作用代表之后的内容都会在外部折叠隐藏。(营造一种神奇的摘要的错觉) 也可以加入模板,省心省事。 章头图片也就是缩略图,主要看这里 https://blog.zhangruipeng.me/hexo-theme-icarus/Configuration/Posts/adding-a-thumbnail-to-your-article/ 设置好以后,文件头里加入thumbnail: ···就可以了。 链接🔗写链接尽量带上协议,不然会被实别成路径。 修改样式:双栏有点麻烦的,看下面这个issue。https://github.com/ppoffice/hexo-theme-icarus/issues/434 TBCD···","link":"/2019/08/05/Misc/Hexo-ICARUS-使用指南与文章模板/"},{"title":"临时笔记栈","text":"事情总会一件一件···完成的。 线段树维护数组 左偏树 https://blog.csdn.net/wang3312362136/article/details/80615874 笛卡尔树 启发式合并https://baijiahao.baidu.com/s?id=1613444794783555531&wfr=spider&for=pc Min_25 筛重新学习笔记https://memset0.cn/min_25 斐波那契堆 划分树, 主席树, 二位线段树, 左偏树 zkw线段树https://blog.csdn.net/keshuqi/article/details/52205884 吉司机线段树https://blog.csdn.net/di4CoveRy/article/details/54582128 KD树https://www.cnblogs.com/earendil/p/8135074.htmlhttps://www.cnblogs.com/earendil/p/8135074.htmlhttps://blog.csdn.net/zjx_adstu/article/details/53366546https://www.luogu.org/problem/P4357 1 到 N 的排列,最长上升子序列(LIS)长度的期望是多少?https://www.zhihu.com/question/266958886/answer/317982906 树形背包 书上做依赖背包","link":"/2019/08/08/Misc/临时笔记栈/"},{"title":"2019.8.6 队内训练","text":"UFBA Practice Session for Brazilian ICPC Regionals - 2018 # Name AC Dsc Stt A Multiset Machine x9 B Color Changing Sofa x140 签到 √- C Renan and Cirque du Soleil x89 推公式 √- D Carnival x25 E Hat-Xor x135 想法/读题 √+ F Renanzinho and His Toys x70 二分/dp/尺取 ○ G Barra Lighthouse x31 H All-In x48 I Colonial Mansions x101 线段树 √- J Soteros x58 K Rei do Cangaço x133 模拟 √- L Code Name Hummingbird x3 M Sorting Machine x41 ○ 代表赛后补题 √+ 代表赛内我通过的 √- 代表赛内不是我做的 √-○代表赛内不是我做的,补了 # = Pen A B C D E F G H I J K L M 1 (64) 5 657 +1 00:29 +2 02:18 + 02:06 -2 -1 +1 03:31 + 01:13","link":"/2019/08/07/CP/Logs/2019-8-6-队内训练/"},{"title":"common sense","text":"下面写一些应该很多人都知道,但是其实刚入门的时候会觉得很迷惑的事情。 __int128 本地运行这个问题我只知道一些情况: 首先这是编译器的原因,GCC / Mingw-w64版本号高了就可以运行了。 Code::Blocks 17.12 如果装了官网的套装版的话是编译不了。(没试过换) CLion配套mingw-w64下载的高版本的toolchain是可以的。 VS高版本的一般都可以。 我是用CLion的。","link":"/2019/09/16/CP/C++/common-sense/"},{"title":"2019.8.12 杭电多校第七场","text":"# name AC dsc stt 6646 A + B = C 23.67%(125/528) 想法/大数/字符串 6647 Bracket Sequences on Tree 18.60%(8/43) 6648 Cuber Occurrence 22.22%(2/9) 6649 Data Structure Problem 25.00%(3/12) 6650 Equation 11.11%(1/9) 6651 Final Exam 47.09%(421/894) 想法/结论 √+ 6652 Getting Your Money Back 16.82%(18/107) 6653 Halt Hater 30.09%(34/113) 6654 Intersection of Prisms 0.00%(0/6) 6655 Just Repeat 18.23%(128/702) 6656 Kejin Player 38.95%(201/516) 概率 √+ ○ 代表赛后补题 √+ 代表赛内我通过的 √- 代表赛内不是我做的 √-○代表赛内不是我做的,补了 Kejin Player很庆幸自己过了。第一次自己推出概率计算题。 问题是从i级升到i+1级有一个花费ai,但是有一个概率pi,代表成功率。如果失败了,就会掉到登记ri。 现在多次询问l到r等级的升级花费。 从i到i+1其实是一个几何概型,每次试验的成功率是pi,那么重复试验到第一次成功的期望可以知道是$\\frac{1}{pi} $这个问题可以去专门学一下几何概型。 可以说平均花了$\\frac{1}{pi} $次才升级,那么$\\frac{1}{pi}-1 $就是降级的次数,降级的次数再乘上从ri升到i的花费就是平均升级失败以后补救的花费,补救+ai就是从i升到i+1的花费。 计算出i到i+1的。","link":"/2019/08/12/CP/Logs/2019-8-12-杭电多校第七场/"},{"title":"2019.8.8 牛客多校第七场","text":"2019牛客暑期多校训练营(第七场) 题号 标题 通过率 dsc stt A String 691/3864 暴力/想法 √- B Irreducible Polynomial 792/2431 结论 √- C Governing sand 520/2702 线段树 √- D Number 985/1499 签到 √- E Find the median 138/1261 F Energy stones 20/149 G Make Shan Happy 3/29 H Pair 143/343 数位DP I Chessboard 34/129 J A+B problem 1061/1919 签到 √- K Function 17/64 ○ 代表赛后补题 √+ 代表赛内我通过的 √- 代表赛内不是我做的 √-○代表赛内不是我做的,补了 A (520/3277) B (682/2135) C (348/1829) D (947/1447) E (74/842) F (14/97) F (14/97) G (2/27) H (83/204) I (16/71) J (997/1793) K (7/40) 01:56:17(-8) 00:36:55 03:14:32 00:09:09 00:04:42 A题意感觉可以变成一个很难得题。 虽然说暴力就可以,但我不是很懂欸= =。 B题意是给一个多项式,判断多项式是否还能因式分解。 有一个结论是,少于三项肯定不行,多于三项必定可以。 判断一下三项得情况就好了(对于n=2); C给n种树,每种树有高度,花费,数量三个属性,现在要求砍掉一些树,使得剩下得树最高的严格大于剩余树的总数的一半。求最小花费。 注意,允许多种树的高度相等。 虽然不是我做的,但是和队友大概的思路是这样的,从高到低枚举高度,这个高度作为最后剩下树的最高高度,每次删掉比这个高度高的树的总花费,再去查询余下的树还需要删去的前m棵最小的花费和。 这个可以用线段树维护一下,但是还没想通。 D给了n和p, 需要输出一个数,刚好有n位数而且整除p. 思路是直接再p后面填0即可。","link":"/2019/08/08/CP/Logs/2019-8-8-牛客多校第七场/"},{"title":"random 伪随机,与更好的伪随机","text":"index > STL > random 主要内容如果支持C++11的话,就像下面这样写随机数: (下面两个写法都表示得到一个$[0,10]$的int型随机数) 123456789101112131415161718#include <chrono>#include <iostream>#include <random>using namespace std;int main() { mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); //minstd_rand rng(chrono::steady_clock::now().time_since_epoch().count()); for (int i = 0; i < 10; ++i) { cout << uniform_int_distribution<int>(0, 10)(rng) << endl; } uniform_int_distribution<int> u(0, 10); for (int i = 0; i < 10; ++i) { cout << u(rng) << endl; }} 123456//简单代码mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());inline int suiJi(const int &l, const int &r) { return uniform_int_distribution<int>(l, r)(rng);} 注释: chrono库提供了一个高精时钟,精确到毫秒,变化周期更短。 uniform_int_distribution可以产生在一个范围上均匀分布的整数值。对于其他分布需求可以看这里 minstd_rand 引擎实现线性同余算法。线性同余引擎一般地快,并对状态的存储要求非常小。 mt19937 引擎实现梅森缠绕器算法。梅森缠绕器较慢且拥有较大的状态存储要求,但只要有正确的参数,就会有最长的的不可重复序列,且拥有最想要的光谱特性(对于给定的想要的定义)。 另外这些东西可能不会很快,也不一定派上用场。 不推荐写法这个生成方法的最大值不一定能覆盖int(与编译环境有关),而质量不高。谨慎使用。 1234567891011#include <time.h>#include <iostream>#include <random>using namespace std;int main() { srand(time(0)); for (int i = 0; i < 10; ++i) { cout<<rand()%10<<endl; } 参考伪随机数生成 Don’t use rand(): a guide to random number generators in C++","link":"/2019/09/16/CP/C++/random 伪随机,与更好的伪随机/"},{"title":"sort 排序","text":"std::sort在漫长的CP岁月中,你可能会学会各种各样的利用std::sort的方法,比如重载运算符,或者写比较函数。同时渐渐忘记如何写快排 甚至一种少见(某种程度上很合理)的内联写法c++11: 123sort(A.begin(), A.end(), [](const Type& a, const Type& b) { return a.day < b.day; }); tbcd…","link":"/2019/09/16/CP/C++/sort 排序/"},{"title":"unordered_map","text":"index > STL > unordered_map 简介内部实现哈希的map,相对于一般map来说,理论上更快。 应用基本和普通map类似,甚至有时候会让人觉得unordered_map更符合我们的需求,因为我们unordered_map的修改查询很快,但遍历很慢,一般我们开map也不用到遍历。 unordered_map对key的要求是需要实现其哈希过程,默认的哈希支持整数类型的key。 缺陷与改进理论上更快的,但有例外,而且很可能会影响到一些特定样例的运行时间。 因为一般的编译器,给unordered_map默认的哈希表是固定的,当然细节挺复杂,详细可以看参考里的那篇博客。 笔者就说一个很简陋的理解: 默认的哈希在处理冲突的时候,是借助固定的哈希表来偏移的,冲突越多需要的时间越多。当有针对性地hack时,最差会退化成$O(N^2)$ 的过程。 为了解决这个问题,一般都会自写哈希过程, 下面是大佬的优秀实现: 12345678910111213141516struct custom_hash { static uint64_t splitmix64(uint64_t x) { // http://xorshift.di.unimi.it/splitmix64.c x += 0x9e3779b97f4a7c15; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb; return x ^ (x >> 31); } size_t operator()(uint64_t x) const { static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count(); return splitmix64(x + FIXED_RANDOM); }};unordered_map<long long, int, custom_hash> safe_map; 可以说,哈希得更好,能提高性能。 参考Blowing up unordered_map, and how to stop getting hacked on it by neal C++ STL: Order of magnitude faster hash tables with Policy Based Data Structures by Chilli","link":"/2019/09/16/CP/C++/unordered_map/"},{"title":"map","text":"index > STL > map 细致的介绍还是前人做的好,我自愧不如。 https://blog.csdn.net/fhb1922702569/article/details/80984774 https://blog.csdn.net/sevenjoin/article/details/81943864 关于比较 列几点我常忽视的细节: 用mp.insert()函数插入已有key的数据时,不能插入数据。而mp[key]引用可改写key对应的value。 map內部的实现自建一颗红黑树,这颗树具有对数据自动排序的功能。(面试可能也会见到相关的提问) 部分应用离散化单纯利用一一对应的特性。 打标记虽然说用 [] 很方便,但是如果就是二元的标记,还是巧用insert/erase/find/count 比较稳妥。(原因有待解释,可能打脸) 特殊二叉搜索树(类红黑树) ——“你其实有一棵随叫随到的红黑树。” 须知: map是有序容器。 mp.begin() 可访问第一个键值对(最值)。 mp[key] 修改某个key的权值,而且改完保持有序。 mp.insert() mp.erase() mp.find() 增删改查都有。 甚至还能自行 mp.lower_bound() mp.upper_bound() 默认lesser<int>升序,想要降序也可以(如果不是原生类型得另外写比较类): 1map<int, int, greater<int> > cnts; 有了上面这些领悟,你这可以解决某些特定的情况: 比如有一群人,每个人的分数不同,找一下最高分的人有几个。 或者,要输出,第一/第二/第三的分数,和对应的人数。 如果嫌简单,再难一点,修改m次分数,每次修改就再求一次答案。 1e6的人数,1e9的分数,可有负分,修改/询问1e6。 可能以上这个题目比较拙劣,如果以后有更好的会再放进来。简单地说,充分利用key排序和修改方便的特性。","link":"/2019/09/16/CP/C++/map/"},{"title":"Connected Components 连通分量及相关算法","text":"引子这是一篇久咕之作。 模板todo 引导首先,假设我们用的是前向星建图。 12345678910struct Edge { int u, v, nx; // ,w} e[MAXN << 1];int head[MAXN], cntEd;inline void addEdge(int u, int v) { e[cntEd] = {u, v, head[u]}; head[u] = cntEd++;} 然后应该写一个DFS。 123456789void tarjan(int u) { for (int i = head[u]; ~i; i = e[i].nx) { int v = e[i].v; if ( ) { } } if ( ) { }} 下面想需要用到什么数组。 1234int low[MAXN]; //LOWLINKint dfn[MAXN], idx; //NUMBERint stk[MAXN], top; //STACK for ccint scc[MAXN], sccnum; //SCC tags 然后想一下每个数组的意义,填进DFS。 12345678910111213141516171819202122232425void tarjan(int u) { dfn[u] = low[u] = ++idx; stk[++top] = u; for (int i = head[u]; ~i; i = e[i].nx) { int v = e[i].v; if (!dfn[v]) { //如果v结点未访问过 tarjan(v); low[u] = min(low[u], low[v]); } else if (!scc[v]) { //如果还在栈内 low[u] = min(low[u], dfn[v]); } } if (low[u] == dfn[u]) { //后代不能找到更浅的点 sccnum++; int x; do { x = stk[top--]; scc[x] = sccnum; } while (x != u); }} 然后学会怎么用它。 12345678910111213141516int main() { memset(head, -1, sizeof head); int n; //input & build graph ... for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } //scc[v] ... return 0;} 好了假装你会了。 当然要真的会了,可以试图去理解一下,DFS中的条件。 例题SCC / Directed Roads给一个n点n边有向无权图。问,有多少种反转边的方案,使图中无简单环。 思路也简单,每条边反转和不反转有$2^n$种方案。假如有一个$m$阶环,有两种方案是不行的——全反转和全不反转。所以对每个$m_i$阶环,其实方案有$2^{m_i}-2$种方案。 其他边都当$2$累乘即可。 我的代码:60847488 Tarjan 1972","link":"/2019/09/20/CP/Graph/Connected-Components-连通分量及相关算法/"},{"title":"operator overloading 重载运算符","text":"index > C++ > Operator Overloading 有些事情好像理所当然,但是又不那么顺利。 构造器()1234567struct point { int x, y; point() {} // default constructor point (int _x, int _y) { x = _x; y = _y; }}; 也可以这样写: 12345struct point { int x, y; point() {} // default constructor point (int x, int y): x(x), y(y) {}}; 注* 括号里的是赋值的参数 好像容易记错。 小于号 <12345678struct point { int x, y; // overloading of < operator bool operator<(const point &rhs) const{ // your main logic for the comparator goes here return make_pair(x,y) < make_pair(rhs.x, rhs.y); }}; 请要小心弱比较的特性(C++: Strict Weak Ordering),肯定有人疑惑过为什么只要定义一个小于号。因为对三种大小关系可以只用一个来实现,只不过等于的状态要比较两次,比如a<b和b<a结果都为假,那么就判断为两者相等。 如果你在定义严格小的过程中,误用了等号,会导致难以预料的结果。而且很可能自己测试对了,某些样例过不了。 可以这样去理解:如果满足条件的时候,本身这个元素,一定放在另外一个元素之前,则这个条件要返还真。 可能有人会觉得) const {这个孤零零的很奇怪,但是这个是必要的语法结构。尤其是在被用于map或者优先队列之类key的时候。 <<注* 必须要全局函数的形式(原理暂略)reference 12345678struct node { int x,y; friend ostream & operator << (ostream &out,node &T);};ostream & operator << (ostream &out,node &T){ out<<T.x<<\" \"<<T.y; return out;} 普通()好像是有括号的计算是需要的(未证实) 1234struct node { int x,y; node operator ()(const node &T){return T;}}; 计算12345678910struct node { int x,y; node ():x(0),y(0){} node (int x,int y):x(x),y(y){} int len(){return x*x+y*y;} node operator - (const node &T){return node(x-T.x,y-T.y);} node operator + (const node &T){return node(x+T.x,y+T.y);} int operator * (const node &T){return x*T.x+y*T.y;} int operator / (const node &T){return x*T.y-y*T.x;}//叉积}; 例题一般都是在坐标运算,或者比较特殊的模拟的时候需要用到这些。CodeForces - 136D :: Mine :: Dalao 参考Using Constructors and comparison function in C++ By PraveenDhinwa","link":"/2019/09/16/CP/C++/operator overloading 重载运算符/"},{"title":"今天我会kruskal重构树了吗?","text":"我并不会,甚至还欠了一屁股博客要读。 咋办? https://www.cnblogs.com/ww3113306/p/9746449.html 在普通的kruskal中,如果一条边连接了在2个不同集合中的点的话,我们将合并这2个点所在集合。而在kruskal重构树中,如果一条边连接了在2个不同集合中的点,我们将新建一个节点出来,并用这个新节点作为一个中转连接这2个集合。如图就是一棵kruskal重构树,方点表示新建出的节点,圆点是原图中的点,方点点权即边权。 https://blog.csdn.net/hwzzyr/article/details/81190442 相信大家都会Kruskal由于重构树中把原树的点权转换成为了新建节点的边权,这一过程是这样实现的。首先对边排序然后使用并查集辅助加边,每新建一条边时:新建节点index(编号从n+1开始)将原有两节点所在集合改为index将原有节点与index连边新建节点的权值为当前边的边权给一下简单的代码 12345678910111213void Ex_Kruskal() { int ind=n,lim=n<<1; sort(e+1,e+1+m); for(int i=1;i<=lim;++i) f[i]=i; for(int i=1;i<=m;++i) { int fx=getfa(e[i].a),fy=getfa(e[i].b); if(fx!=fy) { f[fx]=f[fy]=++ind; val[ind]=e[i].w; add(ind,fx); add(ind,fy); if(ind==lim-1) break; } } return ;} https://blog.csdn.net/niiick/article/details/81952126 https://blog.csdn.net/hwzzyr/article/details/81190442 一道题","link":"/2019/09/24/CP/Graph/今天我会kruskal重构树了吗?/"},{"title":"hdu-6638 线段树 最大子段和","text":"题意给你n个坐标,每个坐标有权值(可正可负),目标是找一个平行于坐标轴矩阵,使得矩阵内的取值求和最大,输出最大值。这个矩阵的坐标可以是非整数的,但是输入是整数的,暗示单个点也是可以被框出来的。 思路比较容易想到的是,先离散化坐标,求一个最大子矩阵好像就可以了。 但是想想这个复杂度,对于一个二维数组求最大子矩阵的话,复杂度是$O(n^3)$的,这里2000个点可以对角线排列,那么二维数组的大小就是2000x2000的,三次的复杂度其实不可接受。 然后就去想,这个题里的点其实比较稀疏,只占二维平面里的一点点空间,而一般的做法第三维是需要一次$O(n)$的遍历来做区间最大子段和的,那么在这个问题里我们可以想办法在更少的时间算出最大子段和,并且避开遍历n次的时间。 做法就是用线段树来维护最大子段和,对于那些落在边界内部的点,可以预处理排序所有的点,这样可尺取地逐步放入边界里的点,放完了就可以用线段树得到当前的答案,然后继续移动边界,把下面的点加入线段树,继续计算答案并更新。 这个线段树的设计可以是单点更新$O(log(n))$,并$O(1)$询问的。总体复杂度$O(n^2log(n))$ 数组说明1234ll sum[TreeSize]; //表示区间和ll lft[TreeSize]; //表示当前区间左边开始连续的最大子段和ll rht[TreeSize]; //表示当前区间右边开始连续的最大子段和ll ans[TreeSize]; //表示当前区间的答案(最大字段和) Pro.ID Exe.Time Exe.Memory Language Author 6638 2308MS 1760K G++ tieway59 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161#include <bits/stdc++.h>#define _debug(x) cerr<< #x << \" = \"<<x <<endlusing namespace std;typedef long long ll;const ll INF = 0x3f3f3f3f;const ll LINF = 0x3f3f3f3f3f3f3f3f;const int MAXN = 4000 + 59;const int TreeSize = MAXN << 2;struct SegTree {#define lson (oo<<1)#define rson (oo<<1|1) int n; ll sum[TreeSize]; ll lft[TreeSize]; ll rht[TreeSize]; ll ans[TreeSize]; void init(int _n) { this->n = _n; build(1, 1, _n); } void build(int oo, int l, int r) { if (l == r) { sum[oo] = lft[oo] = rht[oo] = ans[oo] = 0; return; } int m = (l + r) >> 1; build(lson, l, m); build(rson, m + 1, r); sum[oo] = lft[oo] = rht[oo] = ans[oo] = 0; } void update(int oo, int l, int r, int pos, ll val) { if (l == r && r == pos) { sum[oo] += val;//+val? rht[oo] = max(0ll, sum[oo]); lft[oo] = max(0ll, sum[oo]); ans[oo] = max(0ll, sum[oo]); return; } int m = (l + r) >> 1; if (pos <= m) update(lson, l, m, pos, val); else update(rson, m + 1, r, pos, val); lft[oo] = lft[lson]; rht[oo] = rht[rson]; sum[oo] = sum[lson] + sum[rson]; ans[oo] = max(ans[lson], ans[rson]); ans[oo] = max(ans[oo], rht[lson] + lft[rson]); lft[oo] = max(lft[oo], sum[lson] + lft[rson]); rht[oo] = max(rht[oo], rht[lson] + sum[rson]); if (lft[lson] == sum[lson]) ans[oo] = max(ans[oo], lft[lson] + lft[rson]); if (rht[rson] == sum[rson]) ans[oo] = max(ans[oo], rht[lson] + rht[rson]); } void update(int pos, ll val) { update(1, 1, n, pos, val); } ll query() { return ans[1]; }};SegTree tree;struct point { int x, y, v;} p[MAXN];int dsc[MAXN], dtot;void initDsc() { sort(dsc, dsc + dtot); dtot = unique(dsc, dsc + dtot) - dsc;}int getDsc(const int &val) { return lower_bound(dsc, dsc + dtot, val) - dsc + 1;}int n;ll ans;int Kase;int xmax, ymax;int main() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> Kase; while (Kase--) { ans = 0; dtot = 0; xmax = ymax = 0; cin >> n; for (int i = 1; i <= n; ++i) { cin >> p[i].x >> p[i].y >> p[i].v; dsc[dtot++] = p[i].x; dsc[dtot++] = p[i].y; } initDsc(); for (int i = 1; i <= n; ++i) { p[i].x = getDsc(p[i].x); p[i].y = getDsc(p[i].y); xmax = max(xmax, p[i].x); ymax = max(ymax, p[i].y); } sort(p + 1, p + 1 + n, [](const point &a, const point &b) { if (a.x == b.x)return a.y < b.y; return a.x < b.x; }); for (int l = 1; l <= n; l++) { tree.init(ymax); for (int r = l; r <= n; r++) { tree.update(p[r].y, p[r].v); if (p[r].x != p[r + 1].x) { ans = max(ans, tree.query()); } } while (p[l].x == p[l + 1].x)l++; } cout << ans << '\\n'; } return 0;}/*241 1 502 1 501 2 502 2 -5002-1 1 5-1 1 1 */","link":"/2019/08/08/CP/Solutions/hdu-6638-线段树-最大子段和/"},{"title":"COJ-11B 奇怪dp","text":"题意usiness 题意也有点费解。给你一个递减函数,表示今天x元可以在下一天得到$f(x)$元。然后每天都可以任意选方案,花费当天的钱,来获得最后的钱。不过保证一定是不会有赚的。 问最后一天得到的钱的最大值。 思路我思考了很久,因为收益会递减,所以其实每天拿在手头的钱其实不会很多(数据范围1000,实测2000多足够)。 所以我后来瞎捣鼓出这样的做法: 枚举第$i$天,枚举当天手头有$x$元钱,然后枚举这$x$元钱有j元是做完美投资的,然后转移得到得到最大的最后收到收益(不算手头的钱,所以最后还要加上手头的)。 完美投资是指,这$j$元钱完全花光能拿到的最大收益,所以我预处理了一个完全背包。但是奇怪的点在于,这背包要么就是完全能被占满,要么就全空。(也就是只从0转移来状态) 转移方程看起来很飘,其实意思很简单,因为第二维是下一天刚开始手头有的钱,所以我吧各种剩余和f都叠在一起算了,导致看起来非常崩坏。 $lt[x]$数组看起来很奇怪,我本意是想表示当前x的钱做完全背包后剩余的钱,但是我发现最后改成x必须正好花光才可以转移。导致$lt[x]$要么等于0,要么等于x了。 我是最后瞎改那个变成完美投资才过的,我也不懂为什么。我中间因为做了判复数的情况,剪掉了很多状态,所以虽然复杂度好像似乎$O(nkk)$的,但很接近$O(nmk) $ 看了nikkukun 巨巨的代码,我觉得我在写弟弟代码。 他说这就是一个负权背包的问题(? 主要和我不一样的点在于,他把隔天领钱这一步单独拎出来转移了(比我瞎搞半天求和好) 然后从大到小枚举当前的收益能转移给那些状态,非常合理,我稍微改一点就wa爆。 然后我就不知道如何思考这题了,到现在我还是有很懵逼的点,或许我应该做做负权背包。 代码我的奇怪做法。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384/* * https://www.cometoj.com/contest/67/problem/B?problem_id=3796 * */#include <bits/stdc++.h>#define _debug(x) cerr<<#x << \" = \" << x<<endlusing namespace std;typedef long long ll;const int MOD = -1;const double eps = 1e-3;//const int INF = 0x3f3f3f3f;const int MAXN = 2e3 + 59;const ll INF = 0x3f3f3f3f3f3f;int kase;int n, m, k;ll ans;ll f[MAXN];ll a[MAXN], b[MAXN];ll dp[105][MAXN];ll bg[MAXN];ll lf[MAXN];int main() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> n >> m >> k; for (int i = 0; i <= k; ++i) { cin >> f[i]; } for (int i = 1; i <= m; ++i) { cin >> a[i] >> b[i]; //b[i] += a[i]; } for (int x = 1; x < MAXN; ++x) bg[x] = -INF, lf[x] = x; for (int j = 1; j <= m; ++j) { for (int x = a[j]; x < MAXN; ++x) { if (bg[x] < bg[x - a[j]] + b[j]) { bg[x] = bg[x - a[j]] + b[j]; lf[x] = lf[x - a[j]]; } } } for (int i = 0; i <= n + 2; ++i) for (int x = 0; x < MAXN; ++x) dp[i][x] = -INF; dp[0][0] = 0; for (int i = 0; i <= n; ++i) { for (int x = 0; x < MAXN; ++x) { if (dp[i][x] < 0)continue; for (int j = 0; j <= x; ++j) { if (bg[j] < 0)continue; dp[i + 1][lf[j] + x - j + f[lf[j] + x - j]] = max(dp[i + 1][lf[j] + x - j + f[lf[j] + x - j]], dp[i][x] + bg[j]); } } } for (int l = 0; l < MAXN; ++l) { ans = max(ans, dp[n][l] + l); } cout << ans << endl; return 0;}/* */ nikkukun 的优秀做法: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef unsigned int ui;typedef pair<int, int> pint;const int N = 100 + 5, K = 2000 + 5;const ll INF_LL = 0x3f3f3f3f3f3f3f3f;ll g[N][K];int f[K], a[N], b[N];int main() { ios::sync_with_stdio(0); cin.tie(0); int n, m, upp; cin >> n >> m >> upp; for (int i = 0; i <= upp; i++) cin >> f[i]; for (int i = 1; i <= m; i++) cin >> a[i] >> b[i]; for (int i = 1; i <= n + 1; i++) for (int j = 0; j < K; j++) g[i][j] = -INF_LL; g[1][0] = 0; for (int i = 2; i <= n + 1; i++) { for (int j = 0; j < K; j++) g[i][j + f[j]] = max(g[i][j + f[j]], g[i - 1][j]); for (int j = K - 1; j >= 0; j--) for (int k = 1; k <= m; k++) { if (j - a[k] < 0) continue; g[i][j - a[k]] = max(g[i][j - a[k]], g[i][j] + b[k]); } } ll ans = 0; for (int j = 0; j < K; j++) ans = max(ans, j + g[n + 1][j]); cout << ans; return 0;}","link":"/2019/09/24/CP/Solutions/COJ-11B-usiness-奇怪dp/"},{"title":"contest-888-A 单调栈 前缀","text":"题意给一个01矩阵,然后求一个矩阵的最大全1子矩阵数量,这些矩阵要保证,不被其他全一矩阵包含。 思路 官方题解说的很好了,补充:栈里存的高度会要么大于当前up,要么等于当前up,显然等于的时候是不计答案的,大于的时候需要计答案,并且这个矩形的范围其实是从是当前是ij左边的位置开始。(可能和写法有关)。 这样的题目没有想法感觉非常危险。 代码特别地,我枚举j到m+1可以省一次最后的退栈代码。(注释) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364#include <bits/stdc++.h>#define _debug(x) cerr<<#x<<\" = \"<<x<<endlusing namespace std;typedef long long ll;const int MAXN = 3e3 + 59;typedef long long ll;int n, m;struct { int up, ls;} stk[MAXN];int up[MAXN][MAXN]; //consecutive 1 above (i,j) (included)int pr[MAXN][MAXN]; //pre sum 1 in the left of (i,j) (included)char str[MAXN];int top;ll ans;int main() { ios_base::sync_with_stdio(0); cin.tie(0); ans = 0; cin >> n >> m; for (int i = 1; i <= n; ++i) { cin >> (str + 1); for (int j = 1; j <= m; ++j) { up[i][j] = (str[j] == '1') ? (up[i - 1][j] + 1) : 0; pr[i][j] = pr[i][j - 1] + (str[j] - '0'); } } for (int i = 1; i <= n; ++i) { top = 0; for (int j = 1, k; j <= m + 1; ++j) { k = j; while (top && stk[top].up >= up[i][j]) { k = stk[top].ls; if (stk[top].up > up[i][j]) { int l = stk[top].ls; int r = j - 1; if (pr[i + 1][r] - pr[i + 1][l - 1] != r - l + 1) { ans++; } } top--; } if (up[i][j])stk[++top] = {up[i][j], k}; }// while (top) {// int l = stk[top].ls;// int r = m;// if (pr[i + 1][r] - pr[i + 1][l - 1] != r - l + 1) {// ans++;// }// top--;// } } cout << ans << endl; return 0;}","link":"/2019/08/11/CP/Solutions/contest-888-A-单调栈-前缀/"},{"title":"sweep line 扫描线略知略会","text":"做了一些有相同思路的题,先留个坑。 思路 🌎 2019/8/16 21:48:22我的理解是 对一些事件根据一定顺序排序然后for ——辰巨如是说 待阅读https://codeforces.com/blog/entry/20377 https://blog.csdn.net/xianpingping/article/details/83032798 https://blog.csdn.net/sslz_fsy/article/details/82902644 例题与例程ZJNU 1426(弊校OJ,可能打不开) 12345678910111213141516171819202122232425#include <bits/stdc++.h>using namespace std;int n,cur,ans;map<int, int> cnt;int main() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> n; for (int a, b, i = 1; i <= n; i++) { cin >> a >> b; cnt[a - 1]++; cnt[b]--; } for (auto pi:cnt) { ans = max(ans, cur); cur += pi.second; } cout << ans << endl; return 0;} NCOJ 889D Symmetrical Painting2019牛客暑期多校训练营(第九场)J 12345678910111213141516171819202122232425262728293031323334353637383940414243#include <bits/stdc++.h> #define get0(x) get<0>(x)#define get1(x) get<1>(x)#define _debug(x) cerr<<#x<<\" = \"<<x<<endlusing namespace std;typedef long long ll;const int MAXN = 1e6 + 59;const ll MOD = 1e9 + 7; ll n, ans;vector<tuple<int, int> > pos; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> n; pos.emplace_back(0, 0); for (ll l, r, i = 1; i <= n; i++) { cin >> l >> r; pos.emplace_back(l + l, 1); pos.emplace_back(l + r, -2); pos.emplace_back(r + r, 1); } sort(pos.begin(), pos.end()); ll cur = 0; ll del = 0; ll las = 0; for (auto &p:pos) { cur += (get0(p) - las) * del; del += get1(p); las = get0(p); ans = max(ans, cur); } cout << ans << endl; return 0;} HDU 6627HDU 2019 Multi-University Training Contest 5 这题main函数里其实不复杂,但是分数可真是搞死我了。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193#include <bits/stdc++.h>#define get0(x) get<0>(x)#define get1(x) get<1>(x)#define get2(x) get<2>(x)#define _debug(x) cerr<<#x<<\" = \"<<x<<endlusing namespace std;typedef long long ll;const int MAXN = 1e6 + 59;const ll MOD = 1e9 + 7;const ll INF = 1001;typedef struct frac { ll a, b; frac() : a(0), b(1) {} frac(ll _a) : a(_a), b(1) {} frac(ll _a, ll _b) : a(_a), b(_b) { if (a == 0) { this->b = 1; } if (b < 0) { this->a = -_a; this->b = -_b; } ll g = __gcd(abs(a), abs(b)); this->a /= g; this->b /= g; } frac operator+(const frac &rht) const { return frac(a * rht.b + b * rht.a, b * rht.b); } frac operator-(const frac &rht) const { return frac(a * rht.b - b * rht.a, b * rht.b); } frac operator*(const frac &rht) const { return frac(a * rht.a, b * rht.b); } frac operator/(const frac &rht) const { return frac(a * rht.b, b * rht.a); } bool operator<(const frac &rht) const { return a * rht.b < b * rht.a; } bool operator<=(const frac &rht) const { return a * rht.b <= b * rht.a; } bool operator>(const frac &rht) const { return a * rht.b > b * rht.a; } bool operator>=(const frac &rht) const { return a * rht.b >= b * rht.a; } bool operator==(const frac &rht) const { return a * rht.b == b * rht.a; } friend ostream &operator<<(ostream &out, frac &T); void operator()(ll _a, ll _b) { *this = frac(_a, _b); }} fc;ostream &operator<<(ostream &out, fc &T) { out << T.a << \"/\" << T.b; return out;}// y1<y2fc calc1(fc x1, fc y1, fc x2, fc y2, fc c) { fc dx = x2 - x1; fc dy = y2 - y1; fc dc = c - y1; return x1 + dc * dx / dy;}// y1>y2fc calc2(fc x1, fc y1, fc x2, fc y2, fc c) { fc dx = x2 - x1; fc dy = y1 - y2; fc dc = c - y2; return x2 - dc * dx / dy;}ll n, kase, c;vector<tuple<fc, ll, ll> > pos;vector<fc> ans;int main() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> kase; while (kase--) { ans.clear(); pos.clear(); cin >> n >> c; fc prek(0); fc preb(0); fc lasx(-INF); fc lasy; fc v(c); //pos.emplace_back(-INF, 0, 0); for (ll _a, _b, i = 1; i <= n; i++) { cin >> _a >> _b; prek = prek + fc(-_a); preb = preb + fc(-_b); pos.emplace_back(frac(-_b, _a), 2 * _a, 2 * _b);// +inf ,+k } pos.emplace_back(INF, 0, 0); lasy = prek * lasx + preb; sort(pos.begin(), pos.end()); bool infi = false; for (auto &p:pos) { fc x, y; ll k, b; tie(x, k, b) = p; y = (prek + k) * x + (preb + b); if (lasy == v && y == v) { ans.clear(); infi = true; break; } if (lasy <= v && y > v) { ans.push_back(calc1(lasx, lasy, x, y, v)); } else if (lasy >= v && y < v) { ans.push_back(calc2(lasx, lasy, x, y, v)); } lasy = y; lasx = x; prek = prek + k; preb = preb + b; } if (infi) { cout << \"-1\\n\"; } else if (ans.empty()) { cout << \"0\\n\"; } else { cout << ans.size() << \" \"; for (int i = 0; i < ans.size(); i++) cout << ans[i] << \" \\n\"[i == ans.size() - 1]; } ans.clear(); pos.clear(); } return 0;}/*42 31 21 -13 32 12 22 32 13 54 -13 21 -11 -21 -3 */","link":"/2019/08/16/CP/Misc/sweep-line-扫描线略知略会/"},{"title":"2019-9-18 2019 ICPC UNdC","text":"Name AC Amazon Boring Non-Palindrome √- Common Subsequence √- Do Not Try This Problem Extreme Image Fraction Formula Graduation √- Hardest Challenge Integer Prefix √+ Jail Destruction Kernel Of Love √- Liquid X ○ 代表赛后补题 √+ 代表赛内我通过的 √- 代表赛内不是我做的 √-○代表赛内不是我做的,补了 小结 做F意识到了学python的快乐(然而不会 中间读错题有点伤 python的Fraction不好用 L. Integer Prefix输出最长的纯数字前缀。 123456789101112131415161718#include <bits/stdc++.h>using namespace std;int main() { int T; scanf(\"%d\", &T); while (T--) { int n; scanf(\"%d\", &n); if (n < 3) { printf(\"0\\n\"); continue; } int ans = n / 3 * 2; if (n % 3)ans++; printf(\"%d\\n\", ans); } return 0;} B. Boring Non-Palindromebnc稍微读错了,意思是在串的末尾添加最少的元素,使得串变成回文串。 我们的做法是,做一下Manacher,找到最长的后缀回文串。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#include <bits/stdc++.h>using namespace std; const int N=5005; char ss[N<<2];int p[N<<2]; void manacher(char *s){ int len=strlen(s); for(int i=len;i>=0;--i){ s[i+i+2]=s[i]; s[i+i+1]='#'; } s[0]='*'; int k=1,maxlen=0; for(int i=2;i<len+len+1;++i){ int maxr=k+p[k]-1; p[i]=min(p[2*k-i],max(maxr-i+1,1)); while(s[i-p[i]] == s[i+p[i]])++p[i]; if(i+p[i]>k+p[k])k=i; if(p[i]>maxlen)maxlen=p[i]; }} map<int,string>mp; int main(){ scanf(\"%s\",ss); int len=strlen(ss); manacher(ss); int ed=len*2+1; int pos=0; for(int i=1;i<=ed;i++){ if(p[i]+i-1==ed){ pos=i; break; } } for(int i=1;i<=pos;i++){ if(!(i&1)){ printf(\"%c\",ss[i]); } } for(int i=pos-1;i>1;i--){ if(!(i&1)){ printf(\"%c\",ss[i]); } } printf(\"\\n\"); return 0;} K. Kernel Of Love求一个前n个斐波那契数,有多少对满足四个条件的。可以大胆想一些众所周知的结论。 1234567891011121314151617181920#include <bits/stdc++.h> using namespace std; int main() { int T; scanf(\"%d\", &T); while (T--) { int n; scanf(\"%d\", &n); if (n < 3) { printf(\"0\\n\"); continue; } int ans = n / 3 * 2; if (n % 3)ans++; printf(\"%d\\n\", ans); } return 0;} G. Graduation给n门课的先后关系,以及一个学期能上的课k,求最少需要几个学期才能上完课。 一个课只有一个前驱,并且必须是要上过以后才能选。 思路: 其实是贪心题,把先后关系连成一棵树,优先上掉“深度较大”的课即可。 可以这样理解,如果关系链越长,如果不早点上,就可能把一个学期的k个次数用完,就会更可能把很多课延后,这就不优了。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879#include <bits/stdc++.h>using namespace std;const int N=1e4+4;int n,k; int a[N];int dep[N]; int head[N],cnt;struct _edge{ int v,nxt;}edge[N]; void add_edge(int u,int v){ edge[++cnt].v=v; edge[cnt].nxt=head[u]; head[u]=cnt;} struct node{ int dep,id; bool operator <(const node &a)const { return dep<a.dep; }}p[N]; priority_queue<node>q; void dfs(int u,int fa){ dep[u]=0; for(int i=head[u];~i;i=edge[i].nxt){ int v=edge[i].v; if(v==fa)continue; dfs(v,u); dep[u]=max(dep[u],dep[v]+1); }} bool vis[N];int st[N],top;int main(){ scanf(\"%d%d\",&n,&k); memset(head,-1, sizeof(head)); for(int i=1;i<=n;i++){ scanf(\"%d\",&a[i]); add_edge(a[i],i); } dfs(0,-1); for(int i=0;i<=n;i++){ p[i]={dep[i],i}; } q.push(p[0]); vis[0]= true; int dfn=-1; while (true){ dfn++; top=0; for(int i=1;i<=k&&!q.empty();i++){ st[++top]=q.top().id; q.pop(); } if(!top){ break; } for(int i=1;i<=top;i++){ for(int j=head[st[i]];~j;j=edge[j].nxt){ int v=edge[j].v; if(vis[v])continue; q.push({dep[v],v}); vis[v]=true; } } if(q.empty()){ break; } } printf(\"%d\\n\",dfn); return 0;} D. Do Not Try This Problem这题比较有东西。 给两个串,提问两个串之间的最大公共子序列是否大于等于$0.99N$。 字符集只有4。 似乎是利用字符集和0.99优化一个$O(N^2)$的做法,我不太会··· 可以先想象最长公共子序列的dp,然后去优化它。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657#include <bits/stdc++.h> using namespace std;const int maxn = 1e5 + 5;int dp[2][2005];char a[maxn];char b[maxn];int app[maxn][26]; int getval(int z, int i, int j) { int k = j - i + 1000; return dp[z][k];} void setval(int z, int i, int j, int v) { int k = j - i + 1000; dp[z][k] = v;} int main() { scanf(\"%s%s\", a + 1, b + 1); int n = strlen(a + 1); for (int i = 1; i <= n; i++) { for (int j = 0; j < 26; j++) { app[i][j] = app[i - 1][j]; } app[i][b[i] - 'A'] = i; } int lim = n / 100; int now = 0; int last = 1; int ans; for (int i = 1; i <= n; i++) { now ^= 1; last ^= 1; int l = max(i - lim, 1); int r = min(i + lim, n); int id = a[i] - 'A'; for (int j = l; j <= r; j++) { int temp; if (j == i + lim)temp = getval(last, i - 1, j - 1); else temp = getval(last, i - 1, j); int p = app[j][id]; if (p >= l && p <= r)temp = max(temp, getval(last, i - 1, p - 1)+1); if (j <= i - lim + 1)setval(now, i, j, temp); else if (j == l)setval(now, i, j, temp); else setval(now, i, j, max(temp, getval(now, i, j - 1))); } if (i == n)ans = max(getval(now, i, l), getval(now, i, r)); } //printf(\"%d\\n\",ans); if(ans*100>=n*99)printf(\"Long lost brothers D:\"); else printf(\"Not brothers :(\"); return 0;}","link":"/2019/09/18/CP/Logs/2019-09-18-2019-ICPC-UNdC/"},{"title":"jsk-41402 ICPC Shenyang Pre E","text":"题意link 给一个图,有些点是坏的,有些点是好的。主角自由行动,但会有意识地尽量走多的好点。 每个好点可以取走一个收益,第一次访问到坏点,会等概率跳到与之相邻的某一点。 注意第二次走到坏点就直接结束过程,或者没有更多收益的时候,可以主动结束过程。 起点是1,求收益的期望。 思路相互连通的好点必然可以缩成一块。当缩成一块的时候,第一块的收益必定取完。 而人只能走一次坏点,有些选择是更优的(期望更大),所以有意识的主角只会走期望最大的方案。(如果有多个期望相等的情况,其实仔细一想,等概率去走的时候,收益的期望是一样的,所以只要知道最大是多少即可) 有了以上的理解,只要知道怎么算第一个走到的点的期望怎么算就好了。无非就是所链接块的收益求和乘以概率即可。 有些细节: 起点所在的块的收益是必被取过的,所以如果跳到的点是回到第一块了,收益是0。 如果跳到的点也是坏点,收益是0。 代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114/** solution for https://nanti.jisuanke.com/t/41402**///#include <bits/stdc++.h>#include <iostream>#include <algorithm>#include <vector>#include <string.h>#include <tuple>#define _debug(x) cerr<<#x<<\" = \"<<x<<endlusing namespace std;typedef long long ll;const int MAXN = 1e5 + 59;const int MAXM = 2e5 + 59;const ll MOD = 998244353;const ll INF = 0x0f0f0f0f;int fa[MAXN];int sz[MAXN];int findf(int x) { if (x == fa[x])return x; else { sz[fa[x]] += sz[x]; sz[x] = 0; return fa[x] = findf(fa[x]); }}void mergf(int x, int y) { int fx = findf(x); int fy = findf(y); if (fx == fy)return; if (sz[fx] <= sz[fy]) { sz[fx] += sz[fy]; sz[fy] = 0; fa[fy] = fx; } else { sz[fy] += sz[fx]; sz[fx] = 0; fa[fx] = fy; }}int Kase;int n, m, k;pair<int, int> e[MAXM];int a[MAXN];bool tag[MAXN];bool vis[MAXN];vector<int> g[MAXN];int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); cin >> Kase; while (Kase--) { cin >> n >> m >> k; for (int i = 1; i <= n; ++i) { fa[i] = i; sz[i] = 1; tag[i] = false; vis[i] = false; g[i].clear(); } for (int i = 1; i <= m; ++i) { cin >> e[i].first >> e[i].second; } for (int i = 1; i <= k; ++i) { cin >> a[i]; tag[a[i]] = true; sz[a[i]] = 0; } for (int i = 1; i <= m; ++i) { if (tag[e[i].first] == 0 && tag[e[i].second] == 0) { mergf(e[i].first, e[i].second); } } for (int u, v, i = 1; i <= m; ++i) { tie(u, v) = e[i]; v = findf(v); u = findf(u); if (tag[u] || tag[v]) { g[u].emplace_back(v); g[v].emplace_back(u); } } int start = findf(1); double ans = sz[start]; double tmp = 0; for (auto u:g[start]) { if (vis[u])continue; else vis[u] = true; double sum = 0; for (auto v:g[u])if (v != start)sum += sz[v]; sum /= 1.0 * g[u].size(); if (sum > tmp) tmp = sum; } ans += tmp; printf(\"%.6f\\n\", ans); } return 0;}","link":"/2019/09/16/CP/Solutions/jsk-41402-ICPC-Shenyang-Pre-E/"},{"title":"hdu-6685 Rikka with Coin 贪心 想法","text":"http://acm.hdu.edu.cn/showproblem.php?pid=6685 题意给一个不超过100的数组,每个元素代表价格,需要最少携带多少个,10/20/50/100面额的硬币,可以购买任意一个物品。 思路突破口是,10面额到10个,20面额到5个,50面额到2个,(因为一旦超过就可以用100面额替代更优),好像如果枚举小面额,就大概能知道100面额的有多少个了,枚举次数也才100的复杂度,那就三层for枚举吧。 然后怎么算100有多少个,我是这样做的, 本身再三层for枚举一下,是不是当前的硬币可以凑出当前的ai,如果ai比较大,可以i试着去掉一些100去检查。我们枚举的面额最多是220,所以先降到到200+ai%100检查一下,然后再降到100+ai%100检查一下,就可以了。 代码1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374#include <bits/stdc++.h>//#include <stdio.h>//#include <algorithm>#define per(i, a, b) for(int i=(a);i<=b;i++)#define _debug(x) cerr<<#x<<\" = \"<<x<<endlusing namespace std;typedef long long ll;const int MAXN = 1e5 + 59;const ll MOD = 1e9 + 7;const ll INF = 1e9 + 59;int kase, n, ans;int a[105];int b[105];bool chek(int x, int i, int j, int k) { //10 20 50; per(_10, 0, i) per(_20, 0, j) per(_50, 0, k) if (_10 * 10 + _20 * 20 + _50 * 50 == x) return true; return false;}int main() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> kase; while (kase--) { ans = INF; cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; per(_10, 0, 10) per(_20, 0, 5) per(_50, 0, 2) { int _100 = 0; bool valid = true; for (int i = 1; valid && i <= n; ++i) { if (chek(a[i], _10, _20, _50)) { _100 = max(_100, 0); } else if (a[i] >= 300 && chek(a[i] % 100 + 200, _10, _20, _50)) { _100 = max(_100, a[i] / 100 - 2); } else if (a[i] >= 200 && chek(a[i] % 100 + 100, _10, _20, _50)) { _100 = max(_100, a[i] / 100 - 1); } else if (a[i] >= 100 && chek(a[i] % 100, _10, _20, _50)) { _100 = max(_100, a[i] / 100); } else { valid = false; } } if (valid) ans = min(ans, _10 + _20 + _50 + _100); } if (ans == INF)ans = -1; cout << ans << endl; } return 0;}/* */","link":"/2019/08/19/CP/Solutions/hdu-6685-Rikka-with-Coin-贪心-想法/"},{"title":"hdu-6698 Coins 优先队列 dp","text":"题意给n个数对,每个对可以取两次价值,第一次取得左边的ai,第二次才可以取得右边的bi。 现在要求解2n个问题,问题i表示输出,取i次价值能获得的最大价值。 思路我们集训队的yyj巨巨提出了非常棒的猜想。思考的方向应该是,用优先队列维护剩余的对中,还能取得的最大价值,可能第一次取左边的,也可能是第二次取右边的,由于每对数都是最多有序取两次,每次从前一个状态转移会有比较多的讨论,比如某个位置的元素有没有之前被取过,取过几次。(参考标解)(好吧我不懂) 那么假如我们把两步转移连起来看,可以归纳成两种转移,即取了两个不同位置的元素a或b各一次,或者连续两次取了相同位置的ab元素。 可以大概想一下有这样的转移:$$dp[i]=dp[i-2]+max(node[i].v+node[j].v, node[k].a+node[k].b)$$ 注意上式中ijk都代表剩下可行的位置中的最优解,也就是用优先队列贪心的。 注意v代表a或者b,因为,我们之前可能单独对这个位置取过单次的,所以这里需要做一点判断。(但其实很简单) 注意在这样的转移下,奇数的答案和偶数的答案可以独立转移。dp[1]显然是最大的那个a。 上面的想法略懂即可,如果没有直接能自己敲出来也不要气,这个题确实比较难敲。 下面讲一下我是如何实现的,感谢我的队友Lücy帮我一起Debug。 核心在于,两个优先队列维护(或者说存储),剩余可取的位置最大的价值与位置,这里我用了pair。 我们有两种方案,一种是两个不同位置各取一次,一种是同一个位置取两次。 第一个优先队列a就是为不同位置的方案服务的。但是为了防止和另一种方案产生交集,我初始化在优先队列里塞所有的a,当我使用掉某个a的时候,就把b升级成a再还回到优先队列里。这样就保证队列中所有方案都是在不同位置上的。 第二个优先队列ab与上面类似,不过存的是(ai+bi,i) 。 然used数组记录的是位置i被使用的次数,因为我们两个队列很难一次性保持统一地删元素,那么可以在取元素地时候判掉。 再退回到主函数,应该容易发现奇数偶数的答案可以分别计算,只是起点不同而已,注意计算完ans[1]之后,也要模拟一次存取元素的过程。我的操作是,干脆一开始就把对应位置的,used更新了,也把初始的a变成b,这样可以复用同样的代码。 可能中间有一些奇怪的操作,见谅。 代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103#include <stdio.h>#include <algorithm>#include <queue>#define _debug(x) cerr<<#x<<\" = \"<<x<<endlusing namespace std;typedef long long ll;const int MAXN = 1e5 + 5;const int INF = 0x3f3f3f3f;const ll MOD = 1e9 + 7;typedef long long ll;typedef pair<int, int> node;int n, q;int kase;node a[MAXN];node ab[MAXN];short used[MAXN];int ans[MAXN << 1];void solv(int i) { //数组转优先队列。 priority_queue<node> qa(a, a + n); priority_queue<node> qab(ab, ab + n); for (int x, xi, y, yi, z, zi; i <= 2 * n; i += 2) { ans[i] = ans[i - 2]; z = x = y = -1; zi = xi = yi = -1; while (!qa.empty() && used[qa.top().second] >= 2)qa.pop(); if (!qa.empty() && qa.size() >= 2) { x = qa.top().first; xi = qa.top().second; qa.pop(); while (!qa.empty() && used[qa.top().second] >= 2)qa.pop(); if (!qa.empty()) { y = qa.top().first; yi = qa.top().second; qa.pop(); } else { qa.emplace(x, xi); x = xi = -1; } } while (!qab.empty() && used[qab.top().second] > 0)qab.pop(); if (!qab.empty()) { z = qab.top().first; zi = qab.top().second; qab.pop(); } if (x + y > z) { ans[i] += x + y; used[xi]++; used[yi]++; if (used[xi] == 1)qa.emplace(ab[xi].first - x, xi); if (used[yi] == 1)qa.emplace(ab[yi].first - y, yi); if (z != -1)qab.emplace(z, zi); } else { ans[i] += z; used[zi] += 2; if (x != -1)qa.emplace(x, xi); if (y != -1)qa.emplace(y, yi);// if (z != -1)qa.emplace(z - a[zi].first, zi); } }}int main() { scanf(\"%d\", &kase); while (kase--) { scanf(\"%d\", &n); int maxx = 0, maxi = 0; for (int x, y, i = 0; i < n; i++) { scanf(\"%d%d\", &x, &y); if (x > maxx) { maxx = x; maxi = i; } a[i] = {x, i}; ab[i] = {x + y, i}; } for (int i = 0; i < n; ++i)used[i] = 0; solv(2); for (int i = 0; i < n; ++i)used[i] = 0; ans[1] = maxx; a[maxi].first = ab[maxi].first - a[maxi].first; used[maxi] = 1; solv(3); for (int i = 1; i <= 2 * n; ++i) printf(\"%d%c\", ans[i], \" \\n\"[i == n + n]); } return 0;}","link":"/2019/08/21/CP/Solutions/hdu-6698-Coins-优先队列-dp/"}],"tags":[{"name":"照片","slug":"照片","link":"/tags/照片/"},{"name":"github","slug":"github","link":"/tags/github/"},{"name":"建站","slug":"建站","link":"/tags/建站/"},{"name":"Todo","slug":"Todo","link":"/tags/Todo/"},{"name":"线段树","slug":"线段树","link":"/tags/线段树/"},{"name":"hdu","slug":"hdu","link":"/tags/hdu/"},{"name":"dp","slug":"dp","link":"/tags/dp/"},{"name":"单调栈","slug":"单调栈","link":"/tags/单调栈/"},{"name":"前缀和","slug":"前缀和","link":"/tags/前缀和/"},{"name":"概率","slug":"概率","link":"/tags/概率/"},{"name":"想法","slug":"想法","link":"/tags/想法/"},{"name":"图论","slug":"图论","link":"/tags/图论/"},{"name":"贪心","slug":"贪心","link":"/tags/贪心/"},{"name":"暴力","slug":"暴力","link":"/tags/暴力/"},{"name":"优先队列","slug":"优先队列","link":"/tags/优先队列/"}],"categories":[{"name":"Maven","slug":"Maven","link":"/categories/Maven/"},{"name":"Misc","slug":"Misc","link":"/categories/Misc/"},{"name":"CP","slug":"CP","link":"/categories/CP/"},{"name":"Logs","slug":"CP/Logs","link":"/categories/CP/Logs/"},{"name":"c++","slug":"CP/c","link":"/categories/CP/c/"},{"name":"Graph","slug":"CP/Graph","link":"/categories/CP/Graph/"},{"name":"Solutions","slug":"CP/Solutions","link":"/categories/CP/Solutions/"},{"name":"Misc","slug":"CP/Misc","link":"/categories/CP/Misc/"}]}