卡常神器 手写堆

跟gxy大神还有yzh大神学了学手写的堆,应该比stl的优先队列快很多。
其实就是维护了一个二叉堆,写进结构体里,就没啥了。。。
据说达哥去年NOIP靠这个暴力多骗了分

合并果子。。。。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10010
using namespace std;
int read()
{
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9')f=(x=='-')?-1:1,x=getchar();
    while(x>='0'&&x<='9')sum=(sum<<3)+(sum<<1)+x-'0',x=getchar();
    return sum*f;
}
int n,ans;
template <class qty> struct dui
{
    qty q[N];int sz;
    dui(){sz=0;}
    inline void push(int x)
    {
        q[++sz]=x;
        for(int i=sz,j=i>>1;j;i=j,j>>=1)
            if(q[j]>q[i])swap(q[j],q[i]);
    }
    inline void pop()
    {
        q[1]=q[sz--];
        for(int i=1,j=i<<1;j<=sz;i=j,j<<=1)
        {
            if((j|1)<=sz&&q[j|1]<q[j])j=j|1;
            if(q[i]>q[j])swap(q[i],q[j]);
            else break;
        }
    }
    inline qty top(){return q[1];}
};
dui<int> q;
int main()
{
    n=read();
    for(int i=1,x;i<=n;i++)
    {
        x=read();
        q.push(x);
    }
    int x,y;
    while(q.sz!=1)
    {
        x=q.top();q.pop();
        y=q.top();q.pop();
        ans+=x+y;
        q.push(x+y);
    }
    printf("%d",ans);
}
好的,遵照您的要求,我为您整理了一份极其详细、完整的步骤指南,涵盖了在 Windows 上使用 Visual Studio 2022 和 MASM 从零开始实现 Nova 编译器的全过程。您可以将以下内容复制到文本文件或 Word 文档中。 Nova 编译器实现详细步骤指南 项目概述 目标: 在 Windows 平台上,使用 Visual Studio 2022 (C++) 和 MASM,从零开始实现一个能将 Nova 语言源码编译为原生 x64 可执行文件的编译器。 核心理念: 绝对零开销抽象,追求极致性能。 技术路线: 手写词法分析器、语法分析器、语义分析器,设计自定义中间表示(IR),实现优化,并最终通过 MASM 生成高性能机器码。 阶段零:环境准备与项目初始化 (预计时间: 1天) 步骤 0.1: 安装开发环境 1. 安装 Visual Studio 2022 ◦ 从 https://visualstudio.microsoft.com/下载安装程序。 ◦ 在工作负载中选择 “使用 C++ 的桌面开发”。 ◦ 在 “单个组件” 选项中,确保勾选了 “MSVC v143 - VS 2022 C++ x64/x86 生成工具” 和 “C++ AddressSanitizer” (后者通会自动包含 MASM 汇编器 ml64.exe)。 2. 安装 Git (可选,但强烈推荐) ◦ 从 https://git-scm.com/ 下载并安装,用于版本控制。 步骤 0.2: 验证环境 1. 打开 “Developer Command Prompt for VS 2022” 或 “Developer PowerShell for VS 2022”。 2. 运行以下命令,确认工具可用: cl # 应显示 Microsoft C/C++ 编译器版本信息 ml64 # 应显示 Microsoft Macro Assembler 版本信息 link # 应显示 Microsoft Linker 版本信息 步骤 0.3: 创建项目结构 1. 创建项目根目录,例如 C:\dev\nova-compiler\。 2. 在根目录下创建以下子文件夹结构: nova-compiler/ ├── include/ # 存放所有头文件 (.h) ├── src/ # 存放所有 C++ 源文件 (.cpp) ├── tests/ # 存放测试用例 (.nova 文件) └── lib/ # 未来存放第三方库 (可选) 3. 使用 Visual Studio 2022 创建新项目: ◦ 选择 “空项目”,命名为 NovaCompiler,位置选择刚才创建的 nova-compiler 目录。 ◦ 在 “解决方案资源管理器” 中,右键点击 “源文件” -> “添加” -> “新建项” -> “C++ 文件 (.cpp)”,命名为 main.cpp。 步骤 0.4: 配置项目属性 (至关重要!) 1. 右键点击项目 NovaCompiler -> “属性”。 2. 确保 “配置” 和 “平台” 设置为 “所有配置” 和 “x64”。 3. 进入 “配置属性” -> “高级” -> 将 “字符集” 设置为 “使用多字节字符集”。 这可以避免 Unicode 带来的复杂问题。 4. 点击 “确定”。 阶段一:打通核心流程 - 编译 return 42; (预计时间: 3-5天) 步骤 1.1: 编写最小编译器驱动代码 目标: 创建一个能调用 MASM 和 Linker 的程序框架。 文件: src/main.cpp 代码: #include <iostream> #include <fstream> #include <cstdlib> // for system() int main(int argc, char* argv[]) { std::cout << "Nova Compiler Bootstrapping..." << std::endl; if (argc != 2) { std::cerr << "Usage: novac <sourcefile.nova>" << std::endl; return 1; } // --- 后续阶段将在这里进行真正的编译 --- // 1. 词法分析 (Lexing) // 2. 语法分析 (Parsing) // 3. 代码生成 (Code Generation) // --- 现阶段我们硬编码一个测试程序 --- std::cout << "[模拟] 编译开始: " << argv[1] << std::endl; // 生成 MASM 格式的汇编代码文件 std::ofstream asmFile("output.asm"); asmFile << ".code\n" "main PROC PUBLIC\n" " mov rax, 42 ; Hardcoded return value\n" " ret\n" "main ENDP\n" "END\n"; asmFile.close(); std::cout << "[模拟] 生成汇编: output.asm" << std::endl; // 调用 MASM 汇编器 (ml64.exe) 将 .asm 编译成 .obj std::cout << "调用 MASM 进行汇编..." << std::endl; int masmResult = std::system("ml64 /nologo /c output.asm"); if (masmResult != 0) { std::cerr << "错误: MASM 汇编失败!" << std::endl; return 1; } std::cout << "汇编成功: output.obj" << std::endl; // 调用链接器 (link.exe) 将 .obj 链接成 .exe std::cout << "调用链接器..." << std::endl; int linkResult = std::system("link /nologo /subsystem:console /entry:main output.obj"); if (linkResult != 0) { std::cerr << "错误: 链接失败!" << std::endl; return 1; } std::cout << "链接成功: output.exe" << std::endl; // 运行生成的程序 std::cout << "运行程序..." << std::endl; int runResult = std::system("output.exe"); std::cout << "程序运行完毕,退出代码: " << runResult << " (应为 42)" << std::endl; return 0; } 步骤 1.2: 测试核心流程 1. 在 Visual Studio 中,确保解决方案平台是 x64,然后按 Ctrl + F5 (开始执行不调试)。 2. 程序会运行,并尝试编译一个硬编码的程序。它会成功,因为所有步骤都是写死的。 3. 验证: 在项目目录下,你应该能看到生成的 output.asm, output.obj, output.exe 文件。命令行输出应显示程序退出代码为 42。 4. 结论: 你已成功建立 C++ 驱动器 -> MASM -> LINKER -> .EXE 的自动化流水线。这是项目的基石。 阶段二:实现词法分析器 (Lexer) (预计时间: 3-5天) 步骤 2.1: 定义 Token 类型 文件: include/Token.h 代码: #pragma once #include <string> #include <optional> namespace Nova { enum class TokenType { // 文件结束 EndOfFile, // 关键字 Return, Int, // 字面量 Identifier, IntegerLiteral, // 运算符和分隔符 Semicolon, // ; Colon, // : Equals, // = Plus, // + Minus, // - ParenOpen, // ( ParenClose, // ) BraceOpen, // { BraceClose, // } }; struct Token { TokenType type; std::optional<std::string> value; // 对于标识符、整数字面量,存储其值 int line; int column; Token(TokenType type, int line, int col, std::optional<std::string> val = {}) : type(type), value(val), line(line), column(col) {} }; } // namespace Nova 步骤 2.2: 实现词法分析器 文件: src/Lexer.cpp 代码: (这是一个简化版,需实现 advance, peek, skipWhitespace, number, identifier 等辅助函数) #include "Token.h" #include <string> #include <vector> #include <cctype> #include <map> namespace Nova { class Lexer { std::string source; size_t start = 0; size_t current = 0; int line = 1; int column = 1; std::map<std::string, TokenType> keywords = { {"return", TokenType::Return}, {"int", TokenType::Int}, }; char advance() { /* ... 实现向前移动一个字符 ... */ } bool isAtEnd() const { /* ... */ } char peek() const { /* ... 查看当前字符而不消耗它 ... */ } void skipWhitespace() { /* ... 跳过空格、制表符、换行符 ... */ } Token number() { // 循环读取所有连续的数字 while (std::isdigit(peek())) advance(); std::string numStr = source.substr(start, current - start); return Token(TokenType::IntegerLiteral, line, column, numStr); } Token identifier() { // 循环读取字母、数字、下划线 while (std::isalnum(peek()) || peek() == '_') advance(); std::string idStr = source.substr(start, current - start); // 检查是否是关键字 auto it = keywords.find(idStr); if (it != keywords.end()) { return Token(it->second, line, column); } return Token(TokenType::Identifier, line, column, idStr); } public: Lexer(const std::string& src) : source(src) {} std::vector<Token> scanTokens() { std::vector<Token> tokens; start = current = 0; while (!isAtEnd()) { start = current; char c = advance(); skipWhitespace(); if (isAtEnd()) break; if (std::isdigit(c)) { tokens.push_back(number()); } else if (std::isalpha(c)) { tokens.push_back(identifier()); } else { switch (c) { case ';': tokens.push_back(Token(TokenType::Semicolon, line, column)); break; case ':': tokens.push_back(Token(TokenType::Colon, line, column)); break; case '=': tokens.push_back(Token(TokenType::Equals, line, column)); break; case '+': tokens.push_back(Token(TokenType::Plus, line, column)); break; case '(': tokens.push_back(Token(TokenType::ParenOpen, line, column)); break; case ')': tokens.push_back(Token(TokenType::ParenClose, line, column)); break; case '{': tokens.push_back(Token(TokenType::BraceOpen, line, column)); break; case '}': tokens.push_back(Token(TokenType::BraceClose, line, column)); break; default: std::cerr << "Error: Unexpected character '" << c << "' at line " << line << ", col " << column << std::endl; exit(1); } } } tokens.push_back(Token(TokenType::EndOfFile, line, column)); return tokens; } }; } // namespace Nova 步骤 2.3: 集成并测试词法分析器 1. 修改 main.cpp:包含 #include "Lexer.cpp",并在硬编码生成汇编之前添加代码来读取源文件并调用词法分析器。 2. 创建测试文件:在 tests/ 目录下创建 test_01.nova,内容为 return 42;。 3. 运行测试:修改 main.cpp 参数处理逻辑,使其读取 argv[1] 的文件。运行程序并观察输出的 Token 列表,确认它能正确识别 return, 42, ;。 (Due to the character limit, the document continues in the next message. This covers the first two phases. The subsequent phases would be detailed in a similar step-by-step manner, covering Parser, AST, Sema, IR, Optimization, CodeGen, etc.) 。你有其他的更能优化性能想法吗?
最新发布
09-20
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值