RSA非对称加密算法实现:C#
RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。
常用的开发语言来实现RSA加密:
公钥与私钥
公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。
公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:
# 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式
openssl genrsa -out rsa.pem 1024
# 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用
openssl rsa -in rsa.pem -pubout -out rsa.pub
RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:
# PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中
openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
# PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中
openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出
openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
# PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出
openssl rsa -RSAPublicKey_in -pubout -in rsa.pub
现实中,我们往往从pem、crt、pfx文件获取公私和私钥,crt、pfx的制作可以参考:简单的制作ssl证书,并在nginx和IIS中使用,或者使用现成的:https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7A (提取码:c6tj),密码都是:123456
C#实现
为了方便读取pem、crt、pfx文件中的公私和私钥,这里我使用了第三方的包:Portable.BouncyCastle,可以使用NuGet安装:Install-Package Portable.BouncyCastle
接着,这里我封装了一个RsaHelper辅助类来实现各种RSA加密的过程:
using Org.BouncyCastle.Utilities.IO.Pem;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text; namespace ConsoleApp1
{
public class RsaHelper
{ #region key
/// <summary>
/// 将秘钥保存到Pem文件
/// </summary>
/// <param name="isPrivateKey"></param>
/// <param name="buffer"></param>
/// <param name="pemFileName"></param>
/// <returns></returns>
public static void WriteToPem(byte[] buffer, bool isPrivateKey, string pemFileName)
{
PemObject pemObject = new PemObject(isPrivateKey ? "RSA PRIVATE KEY" : "RSA PUBLIC KEY", buffer);
if (File.Exists(pemFileName))
{
File.Delete(pemFileName);
}
using (var fileStream = new FileStream(pemFileName, FileMode.OpenOrCreate, FileAccess.Write))
{
using (var sw = new StreamWriter(fileStream))
{
var writer = new PemWriter(sw);
writer.WriteObject(pemObject);
sw.Flush();
}
}
}
/// <summary>
/// 从Pem文件中读取秘钥
/// </summary>
/// <param name="pemFileName"></param>
/// <returns></returns>
public static byte[] ReadFromPem(string pemFileName)
{
using (var fileStream = new FileStream(pemFileName, FileMode.Open, FileAccess.Read))
{
using (var sw = new StreamReader(fileStream))
{
var writer = new PemReader(sw);
return writer.ReadPemObject().Content;
}
}
} /// <summary>
/// 从xml中读取秘钥
/// </summary>
/// <param name="xml"></param>
/// <param name="isPrivateKey"></param>
/// <param name="usePkcs8"></param>
/// <returns></returns>
public static byte[] ReadFromXml(string xml, bool isPrivateKey, bool usePkcs8)
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(xml);
if (isPrivateKey)
{
return usePkcs8 ? rsa.ExportPkcs8PrivateKey() : rsa.ExportRSAPrivateKey();
}
return usePkcs8 ? rsa.ExportSubjectPublicKeyInfo() : rsa.ExportRSAPublicKey();
}
}
/// <summary>
/// 将秘钥保存到xml中
/// </summary>
/// <param name="buffer"></param>
/// <param name="isPrivateKey"></param>
/// <param name="usePkcs8"></param>
/// <returns></returns>
public static string WriteToXml(byte[] buffer, bool isPrivateKey, bool usePkcs8)
{
using (var rsa = CreateRSACryptoServiceProvider(buffer, isPrivateKey, usePkcs8))
{
return rsa.ToXmlString(isPrivateKey);
}
} /// <summary>
/// 获取RSA非对称加密的Key
/// </summary>
/// <param name="publicKey"></param>
/// <param name="privateKey"></param>
public static void GenerateRsaKey(bool usePKCS8, out byte[] publicKey, out byte[] privateKey)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.KeySize = 1024;//1024位
if (usePKCS8)
{
//使用pkcs8填充方式导出
publicKey = rsa.ExportSubjectPublicKeyInfo();//公钥
privateKey = rsa.ExportPkcs8PrivateKey();//私钥
}
else
{
//使用pkcs1填充方式导出
publicKey = rsa.ExportRSAPublicKey();//公钥
privateKey = rsa.ExportRSAPrivateKey();//私钥
}
}
}
/// <summary>
/// 从Pfx文件获取RSA非对称加密的Key
/// </summary>
/// <param name="pfxFileName"></param>
/// <param name="publicKey"></param>
/// <param name="privateKey"></param>
public static void ReadFromPfx(string pfxFileName, string password, out byte[] publicKey, out byte[] privateKey)
{
X509Certificate2 x509Certificate2 = new X509Certificate2(pfxFileName, password, X509KeyStorageFlags.Exportable);
publicKey = x509Certificate2.GetRSAPublicKey().ExportRSAPublicKey();
privateKey = x509Certificate2.GetRSAPrivateKey().ExportRSAPrivateKey();
}
/// <summary>
/// 从Crt文件中读取公钥
/// </summary>
/// <param name="crtFileName"></param>
/// <param name="password"></param>
/// <returns></returns>
public static byte[] ReadPublicKeyFromCrt(string crtFileName, string password)
{
X509Certificate2 x509Certificate2 = new X509Certificate2(crtFileName, password, X509KeyStorageFlags.Exportable);
var publicKey = x509Certificate2.GetRSAPublicKey().ExportRSAPublicKey();
return publicKey;
}
#endregion #region Pkcs1 and Pkcs8 /// <summary>
/// Pkcs1转Pkcs8
/// </summary>
/// <param name="isPrivateKey"></param>
/// <param name="buffer"></param>
/// <returns></returns>
public static byte[] Pkcs1ToPkcs8(bool isPrivateKey, byte[] buffer)
{
using (var rsa = new RSACryptoServiceProvider())
{
if (isPrivateKey)
{
rsa.ImportRSAPrivateKey(buffer, out _);
return rsa.ExportPkcs8PrivateKey();
}
else
{
rsa.ImportRSAPublicKey(buffer, out _);
return rsa.ExportSubjectPublicKeyInfo();
}
}
}
/// <summary>
/// Pkcs8转Pkcs1
/// </summary>
/// <param name="isPrivateKey"></param>
/// <param name="buffer"></param>
/// <returns></returns>
public static byte[] Pkcs8ToPkcs1(bool isPrivateKey, byte[] buffer)
{
using (var rsa = new RSACryptoServiceProvider())
{
if (isPrivateKey)
{
rsa.ImportPkcs8PrivateKey(buffer, out _);
return rsa.ExportRSAPrivateKey();
}
else
{
rsa.ImportSubjectPublicKeyInfo(buffer, out _);
return rsa.ExportRSAPublicKey();
}
}
} #endregion #region RSA /// <summary>
/// 获取一个RSACryptoServiceProvider
/// </summary>
/// <param name="isPrivateKey"></param>
/// <param name="buffer"></param>
/// <param name="usePkcs8"></param>
/// <returns></returns>
public static RSACryptoServiceProvider CreateRSACryptoServiceProvider(byte[] buffer, bool isPrivateKey, bool usePkcs8 = false)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
if (isPrivateKey)
{
if (usePkcs8)
rsa.ImportPkcs8PrivateKey(buffer, out _);
else
rsa.ImportRSAPrivateKey(buffer, out _);
}
else
{
if (usePkcs8)
rsa.ImportSubjectPublicKeyInfo(buffer, out _);
else
rsa.ImportRSAPublicKey(buffer, out _);
}
return rsa;
} /// <summary>
/// RSA公钥加密
/// </summary>
/// <param name="value">待加密的明文</param>
/// <param name="publicKey">公钥</param>
/// <param name="usePkcs8">是否使用pkcs8填充</param>
/// <returns></returns>
public static string RsaEncrypt(string value, byte[] publicKey, bool usePkcs8 = false)
{
if (string.IsNullOrEmpty(value)) return value; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(publicKey, false, usePkcs8))
{
var buffer = Encoding.UTF8.GetBytes(value);
buffer = rsa.Encrypt(buffer, false); //使用hex格式输出数据
StringBuilder result = new StringBuilder();
foreach (byte b in buffer)
{
result.AppendFormat("{0:x2}", b);
}
return result.ToString();
//或者使用下面的输出
//return BitConverter.ToString(buffer).Replace("-", "").ToLower();
}
}
/// <summary>
/// RSA私钥解密
/// </summary>
/// <param name="value">密文</param>
/// <param name="privateKey">私钥</param>
/// <param name="usePkcs8">是否使用pkcs8填充</param>
/// <returns></returns>
public static string RsaDecrypt(string value, byte[] privateKey, bool usePkcs8 = false)
{
if (string.IsNullOrEmpty(value)) return value; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(privateKey, true, usePkcs8))
{
//转换hex格式数据为byte数组
var buffer = new byte[value.Length / 2];
for (var i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)Convert.ToInt32(value.Substring(i * 2, 2), 16);
}
buffer = rsa.Decrypt(buffer, false);
return Encoding.UTF8.GetString(buffer);
}
}
/// <summary>
/// RSA私钥生成签名
/// </summary>
/// <param name="value">原始值</param>
/// <param name="publicKey">公钥</param>
/// <param name="halg">签名hash算法:SHA,SHA1,MD5,SHA256,SHA384,SHA512</param>
/// <param name="usePkcs8">是否使用pkcs8填充</param>
/// <returns></returns>
public static string Sign(string value, byte[] privateKey, string halg = "MD5", bool usePkcs8 = false)
{
if (string.IsNullOrEmpty(value)) return value; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(privateKey, true, usePkcs8))
{
byte[] buffer = Encoding.UTF8.GetBytes(value);
buffer = rsa.SignData(buffer, HashAlgorithm.Create(halg)); //使用hex格式输出数据
StringBuilder result = new StringBuilder();
foreach (byte b in buffer)
{
result.AppendFormat("{0:x2}", b);
}
return result.ToString();
//或者使用下面的输出
//return BitConverter.ToString(buffer).Replace("-", "").ToLower();
}
}
/// <summary>
/// RSA公钥验证签名
/// </summary>
/// <param name="value">原始值</param>
/// <param name="publicKey">公钥</param>
/// <param name="signature">签名</param>
/// <param name="halg">签名hash算法:SHA,SHA1,MD5,SHA256,SHA384,SHA512</param>
/// <param name="usePkcs8">是否使用pkcs8填充</param>
/// <returns></returns>
public static bool Verify(string value, byte[] publicKey, string signature, string halg = "MD5", bool usePkcs8 = false)
{
if (string.IsNullOrEmpty(value)) return false; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(publicKey, false, usePkcs8))
{
//转换hex格式数据为byte数组
var buffer = new byte[signature.Length / 2];
for (var i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)Convert.ToInt32(signature.Substring(i * 2, 2), 16);
}
return rsa.VerifyData(Encoding.UTF8.GetBytes(value), HashAlgorithm.Create(halg), buffer);
}
}
#endregion
}
}
RsaHelper
可以使用生成RSA的公私秘钥:
//通过程序生成
RsaHelper.GenerateRsaKey(usePKCS8, out publicKey, out privateKey);
生成秘钥后,需要保存,一般保存到pem文件中:
//保存到Pem文件,filePath是文件目录
RsaHelper.WriteToPem(publicKey, false, Path.Combine(filePath, "rsa.pub"));
RsaHelper.WriteToPem(privateKey, true, Path.Combine(filePath, "rsa.pem"));
还以将公钥和私钥输出为xml格式:
//保存到xml中
string publicKeyXml = RsaHelper.WriteToXml(publicKey, false, usePKCS8);
string privateKeyXml = RsaHelper.WriteToXml(privateKey, true, usePKCS8);
保存到pem文件和xml中后,也可以从中读取:
//从Pem文件获取,filePath是文件目录
publicKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pub"));
privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pem")); //从xml中读取
publicKey = RsaHelper.ReadFromXml(publicKeyXml, false, usePKCS8);
privateKey = RsaHelper.ReadFromXml(privateKeyXml, true, usePKCS8);
还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:
//从crt文件读取,filePath是文件目录
publicKey = RsaHelper.ReadPublicKeyFromCrt(Path.Combine(filePath, "demo.crt"), "");
privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "demo.key"));
pfx文件中包含了公钥和私钥,可以很方便就读取到:
//从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
RsaHelper.ReadFromPfx(Path.Combine(filePath, "demo.pfx"), "123456", out publicKey, out privateKey);
有时候我们还可能需要进行秘钥的转换:
// Pkcs8格式公钥转换为Pkcs1格式公钥
publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey);
// Pkcs8格式私钥转换为Pkcs1格式私钥
privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey);
// Pkcs1格式公钥转换为Pkcs8格式公钥
publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey);
// Pkcs1格式私钥转换为Pkcs8格式私钥
privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey);
有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:
string encryptText = RsaHelper.RsaEncrypt(text, publicKey, usePKCS8);
Console.WriteLine($"【{text}】经过【RSA】加密后:{encryptText}"); string decryptText = RsaHelper.RsaDecrypt(encryptText, privateKey, usePKCS8);
Console.WriteLine($"【{encryptText}】经过【RSA】解密后:{decryptText}"); string signature = RsaHelper.Sign(text, privateKey, "MD5", usePKCS8);
Console.WriteLine($"【{text}】经过【RSA】签名后:{signature}"); bool result = RsaHelper.Verify(text, publicKey, signature, "MD5", usePKCS8);
Console.WriteLine($"【{text}】的签名【{signature}】经过【RSA】验证后结果是:{result}");
完整的demo代码:
using System;
using System.IO; namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string text = "上山打老虎";
bool usePKCS8 = true;// usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥
string filePath = Directory.GetCurrentDirectory();
Console.WriteLine($"文件路径:{filePath}");// 存放pem,crt,pfx等文件的目录
byte[] publicKey, privateKey;// 公钥和私钥 //通过程序生成
RsaHelper.GenerateRsaKey(usePKCS8, out publicKey, out privateKey);
//从Pem文件获取,filePath是文件目录
//publicKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pub"));
//privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pem")); //从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
//RsaHelper.ReadFromPfx(Path.Combine(filePath, "demo.pfx"), "123456", out publicKey, out privateKey);
//从crt文件读取,filePath是文件目录
//publicKey = RsaHelper.ReadPublicKeyFromCrt(Path.Combine(filePath, "demo.crt"), "");
//privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "demo.key")); //保存到Pem文件,filePath是文件目录
RsaHelper.WriteToPem(publicKey, false, Path.Combine(filePath, "rsa.pub"));
RsaHelper.WriteToPem(privateKey, true, Path.Combine(filePath, "rsa.pem")); //保存到xml中
string publicKeyXml = RsaHelper.WriteToXml(publicKey, false, usePKCS8);
string privateKeyXml = RsaHelper.WriteToXml(privateKey, true, usePKCS8); //从xml中读取
publicKey = RsaHelper.ReadFromXml(publicKeyXml, false, usePKCS8);
privateKey = RsaHelper.ReadFromXml(privateKeyXml, true, usePKCS8); // Pkcs8格式公钥转换为Pkcs1格式公钥
publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey);
// Pkcs8格式私钥转换为Pkcs1格式私钥
privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey);
// Pkcs1格式公钥转换为Pkcs8格式公钥
publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey);
// Pkcs1格式私钥转换为Pkcs8格式私钥
privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey); string encryptText = RsaHelper.RsaEncrypt(text, publicKey, usePKCS8);
Console.WriteLine($"【{text}】经过【RSA】加密后:{encryptText}"); string decryptText = RsaHelper.RsaDecrypt(encryptText, privateKey, usePKCS8);
Console.WriteLine($"【{encryptText}】经过【RSA】解密后:{decryptText}"); string signature = RsaHelper.Sign(text, privateKey, "MD5", usePKCS8);
Console.WriteLine($"【{text}】经过【RSA】签名后:{signature}"); bool result = RsaHelper.Verify(text, publicKey, signature, "MD5", usePKCS8);
Console.WriteLine($"【{text}】的签名【{signature}】经过【RSA】验证后结果是:{result}");
}
}
}
RSA非对称加密算法实现:C#的更多相关文章
- RSA—非对称加密算法
RSA:非对称加密算法加解密原理如下:已知:p,q,n,e,d,m,c其中:p与q互为大质数,n=p*q 公钥Pk(n,e):加密使用,是公开的 私钥Sk(n,d):解密使用,不公开 c:明文 m:密 ...
- SSH加密原理、RSA非对称加密算法学习与理解
首先声明一下,这里所说的SSH,并不是Java传统的三大框架,而是一种建立在应用层和传输层基础上的安全外壳协议,熟悉Linux的朋友经常使 用到一 个SSH Secure Shell Cilent的工 ...
- RSA非对称加密算法实现过程
RSA非对称加密算法实现过程 非对称加密算法有很多,RSA算法就是其中比较出名的算法之一,下面是具体实现过程 <?php /** */ class Rsa { /** * private key ...
- RSA非对称加密算法实现:Python
RSA是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院工作.RSA ...
- RSA非对称加密算法实现:Golang
RSA是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院工作.RSA ...
- RSA非对称加密算法实现:Java
RSA是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院工作.RSA ...
- RSA 非对称加密算法简述
RSA概述 首先看这个加密算法的命名.很有意思,它其实是三个人的名字.早在1977年由麻省理工学院的三位数学家Rivest.Shamir 和 Adleman一起提出了这个加密算法,并且用他们三个人姓氏 ...
- .NET Core加解密实战系列之——RSA非对称加密算法
目录 简介 功能依赖 生成RSA秘钥 PKCS1格式 PKCS8格式 私钥操作 PKCS1与PKCS8格式互转 PKCS1与PKCS8私钥中提取公钥 PEM操作 PEM格式密钥读取 PEM格式密钥写入 ...
- 【转载】非对称加密过程详解(基于RSA非对称加密算法实现)
1.非对称加密过程: 假如现实世界中存在A和B进行通讯,为了实现在非安全的通讯通道上实现信息的保密性.完整性.可用性(即信息安全的三个性质),A和B约定使用非对称加密通道进行通讯,具体 ...
随机推荐
- mysql死锁com.mysql.cj.jdbc.exception.MYSQLTransactionRollbackException Deadlock found when trying to get lock;try restarting transaction
1.生产环境出现以下报错 该错误发生在update操作中,该表并未建立索引,也就是只有InnoDB默认的主键索引,发生错误的程序是for循环中update. 什么情况下会出现Deadlock foun ...
- Appium获取toast消息遇到的问题(一)
一.运行错误 Android获取toast,需要在参数里设置automationName:Uiautomator2 1 # 设置设备的信息 2 desired_caps = { 3 'platform ...
- ICCV2021 | 简单有效的长尾视觉识别新方案:蒸馏自监督(SSD)
前言 本文提出了一种概念上简单但特别有效的长尾视觉识别的多阶段训练方案,称为蒸馏自监督(Self Supervision to Distillation, SSD).在三个长尾识别基准:Ima ...
- jenkins实例 nodejs项目
目录 一.案例1 二.案例2 一.案例1 使用shell方式 #清理上一次版本,拉取新代码 rm -rf /server/admin-web cd /server git clone http://g ...
- java JDK8 时间处理
目录 时间格式化 LocalDate:年月日 LocalTime:时分秒毫秒 LocalDateTime:年月日时分秒 Instant:纳秒时间戳 Duration:两时间间隔 Duration:处理 ...
- jetbrain家的fleet(已获得预览权限)直接对标vscode , fleet有望超过vscode吗?今天我们实际操作下
申请预览版 等待了一周终于得到了预览版的机会 今天就来简单使用下. 前言 工程管理大多使用的是maven , 在maven之前还有ant 这个应该已经没多少人在使用了,或者说新人基本不在使用ant , ...
- CF748A Santa Claus and a Place in a Class 题解
Content 圣诞老人坐在一个桌子被摆成 \(m\) 行 \(n\) 列的教室里.每个桌子有两个座位,每个位置从左往右都有编号,依次为 \(1,2,3,...,2\times n\times m\) ...
- LuoguP7892 『JROI-3』R.I.P. 题解
Update \(\texttt{2021.10.11}\) 修改了一处公式错误,麻烦管理重新审核一下这篇已审核通过文章. Content 你在一个无限大的格子平面上,并且有 \(m\) 个长度为 \ ...
- CF755C PolandBall and Forest 题解
Content 给定无向图的 \(n\) 个点的父亲节点,求无向图的联通块个数. 数据范围:\(1\leqslant n\leqslant 10^4\). Solution 并查集模板题. 我们将在当 ...
- CF652B z-sort 题解
Content 定义一个数组是 \(\text{Z}\) 排序过的,当且仅当: 对于所有的 \(i=2k+1(k\in\mathbb Z)\),\(a_i\leqslant a_{i-1}\). 对于 ...