<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>算法刷题记录</title>
<style>
:root {
--bg: #f8f9fa;
--card-bg: #ffffff;
--text: #2c3e50;
--text-secondary: #5a6c7d;
--border: #e2e8f0;
--accent: #4f6ef7;
--accent-hover: #3b54d4;
--tag-green: #e6f7ed;
--tag-green-text: #1a7f4b;
--tag-orange: #fff7ed;
--tag-orange-text: #b3531a;
--tag-red: #fef2f2;
--tag-red-text: #b91c1c;
--tag-blue: #eff6ff;
--tag-blue-text: #1d4ed8;
--tag-purple: #faf5ff;
--tag-purple-text: #6d28d9;
--tag-gray: #f3f4f6;
--tag-gray-text: #4b5563;
--row-hover: #f1f5f9;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04);
--radius: 10px;
--radius-sm: 6px;
--transition: 0.2s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
background: #f0f2f5;
color: var(--text);
line-height: 1.6;
min-height: 100vh;
padding: 16px;
}
.container {
max-width: 1300px;
margin: 0 auto;
}
/* Header Card */
.header {
background: var(--card-bg);
border-radius: var(--radius);
padding: 20px 24px;
margin-bottom: 16px;
box-shadow: var(--shadow);
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 14px;
border: 1px solid var(--border);
}
.header-left {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.header-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #4f6ef7, #7c5cfc);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #fff;
flex-shrink: 0;
}
.header h1 {
font-size: 1.35rem;
font-weight: 700;
color: #1a202c;
letter-spacing: -0.3px;
white-space: nowrap;
}
.header .stats {
display: flex;
gap: 10px;
flex-wrap: wrap;
font-size: 0.85rem;
color: var(--text-secondary);
}
.header .stats span {
background: #f8fafc;
padding: 4px 10px;
border-radius: 20px;
font-weight: 500;
white-space: nowrap;
border: 1px solid #e8ecf1;
}
.header .stats .count-num {
font-weight: 700;
color: var(--accent);
font-size: 1rem;
}
/* Controls */
.controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.search-box {
position: relative;
}
.search-box input {
padding: 9px 14px 9px 36px;
border: 1.5px solid #dde1e7;
border-radius: 24px;
font-size: 0.9rem;
width: 240px;
transition: var(--transition);
background: #fafbfc;
color: var(--text);
outline: none;
}
.search-box input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(79, 110, 247, 0.1);
background: #fff;
width: 280px;
}
.search-box .search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #94a3b8;
font-size: 15px;
pointer-events: none;
}
.btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 9px 18px;
border-radius: 24px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
border: none;
transition: var(--transition);
white-space: nowrap;
text-decoration: none;
letter-spacing: 0.2px;
}
.btn-download {
background: var(--accent);
color: #fff;
box-shadow: 0 2px 6px rgba(79, 110, 247, 0.3);
}
.btn-download:hover {
background: var(--accent-hover);
box-shadow: 0 4px 14px rgba(79, 110, 247, 0.4);
transform: translateY(-1px);
}
.btn-download:active {
transform: translateY(0);
}
.btn-outline {
background: #fff;
color: var(--text);
border: 1.5px solid #dde1e7;
}
.btn-outline:hover {
background: #f8fafc;
border-color: #c4cad4;
}
.btn-sm {
padding: 5px 12px;
font-size: 0.78rem;
border-radius: 16px;
}
/* Table Card */
.table-card {
background: var(--card-bg);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
border: 1px solid var(--border);
}
.table-wrapper {
overflow-x: auto;
max-height: 75vh;
overflow-y: auto;
scroll-behavior: smooth;
}
.table-wrapper::-webkit-scrollbar {
height: 6px;
width: 6px;
}
.table-wrapper::-webkit-scrollbar-track {
background: transparent;
}
.table-wrapper::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
.table-wrapper::-webkit-scrollbar-thumb:hover {
background: #a0aec0;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.88rem;
min-width: 800px;
table-layout: auto;
}
thead {
position: sticky;
top: 0;
z-index: 10;
background: #fafbfc;
}
thead th {
padding: 13px 14px;
text-align: left;
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #5a6c7d;
border-bottom: 2px solid #e2e8f0;
white-space: nowrap;
cursor: pointer;
user-select: none;
transition: background 0.15s;
position: relative;
}
thead th:hover {
background: #eef1f6;
color: #2c3e50;
}
thead th .sort-arrow {
margin-left: 4px;
font-size: 0.65rem;
opacity: 0.4;
transition: opacity 0.15s;
}
thead th.sorted .sort-arrow {
opacity: 1;
color: var(--accent);
}
thead th.col-id {
width: 90px;
min-width: 80px;
}
thead th.col-date {
width: 80px;
min-width: 70px;
}
thead th.col-diff {
width: 130px;
min-width: 110px;
}
thead th.col-rating {
width: 90px;
min-width: 70px;
}
thead th.col-algo {
min-width: 240px;
}
tbody tr {
transition: background 0.12s;
border-bottom: 1px solid #f1f5f9;
}
tbody tr:hover {
background: var(--row-hover);
}
tbody tr:last-child {
border-bottom: none;
}
tbody td {
padding: 11px 14px;
vertical-align: middle;
color: #334155;
}
.prob-id {
font-family: 'SF Mono', 'Cascadia Code', 'Consolas', 'Menlo', 'Monaco', 'Microsoft YaHei', monospace;
font-weight: 700;
color: #1e293b;
font-size: 0.9rem;
letter-spacing: -0.2px;
}
.prob-id a {
color: var(--accent);
text-decoration: none;
transition: color 0.15s;
}
.prob-id a:hover {
color: var(--accent-hover);
text-decoration: underline;
text-underline-offset: 3px;
}
/* Tags */
.tag {
display: inline-block;
padding: 4px 10px;
border-radius: 14px;
font-size: 0.76rem;
font-weight: 600;
letter-spacing: 0.2px;
white-space: nowrap;
}
.tag-diff-pup {
background: #e8f5e9;
color: #2e7d32;
}
.tag-diff-pup-plus {
background: #dcedc8;
color: #33691e;
}
.tag-diff-improve {
background: #fff3e0;
color: #e65100;
}
.tag-diff-improve-plus {
background: #ffe0b2;
color: #bf360c;
}
.tag-diff-prov {
background: #fce4ec;
color: #880e4f;
}
.tag-diff-noi {
background: #ede7f6;
color: #4527a0;
}
.tag-diff-ct {
background: #ffebee;
color: #b71c1c;
font-weight: 700;
}
.tag-rating-good {
background: #e6f7ed;
color: #1a7f4b;
}
.tag-rating-great {
background: #d4f1e4;
color: #0d5e33;
font-weight: 700;
}
.tag-rating-bad {
background: #fef2f2;
color: #b91c1c;
}
.tag-rating-hard {
background: #fff7ed;
color: #c2410c;
font-weight: 700;
}
.tag-rating-water {
background: #f0f9ff;
color: #0369a1;
}
.algo-list {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.algo-tag {
display: inline-block;
background: #f1f5f9;
color: #475569;
padding: 3px 8px;
border-radius: 10px;
font-size: 0.75rem;
font-weight: 500;
white-space: nowrap;
letter-spacing: 0.1px;
border: 1px solid #e8ecf1;
}
/* Toast */
.toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: #1e293b;
color: #fff;
padding: 12px 22px;
border-radius: 28px;
font-size: 0.88rem;
font-weight: 600;
letter-spacing: 0.3px;
z-index: 1000;
opacity: 0;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.no-results {
text-align: center;
padding: 40px 20px;
color: #94a3b8;
font-size: 0.95rem;
}
.no-results .icon {
font-size: 2.5rem;
display: block;
margin-bottom: 8px;
}
@media (max-width: 768px) {
.header {
flex-direction: column;
align-items: flex-start;
}
.controls {
width: 100%;
}
.search-box input {
width: 100%;
}
.search-box input:focus {
width: 100%;
}
.table-wrapper {
max-height: 60vh;
}
table {
font-size: 0.8rem;
min-width: 650px;
}
thead th,
tbody td {
padding: 8px 10px;
}
.btn {
padding: 7px 14px;
font-size: 0.82rem;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header">
<div class="header-left">
<div class="header-icon">📋</div>
<div>
<h1>算法刷题记录</h1>
<div class="stats">
<span>总计 <strong class="count-num" id="totalCount">0</strong> 题</span>
<span>日期范围 <strong>05-16 ~ 07-15</strong></span>
</div>
</div>
</div>
<div class="controls">
<div class="search-box">
<span class="search-icon">🔍</span>
<input type="text" id="searchInput" placeholder="搜索题目编号、算法、备注..." autocomplete="off">
</div>
<button class="btn btn-download" onclick="downloadCSV()" title="下载为CSV文件(可用Excel打开)">
📥 下载CSV
</button>
<button class="btn btn-outline btn-sm" onclick="resetTable()" title="清除搜索与排序">
🔄 重置
</button>
</div>
</div>
<!-- Table -->
<div class="table-card">
<div class="table-wrapper" id="tableWrapper">
<table id="dataTable">
<thead>
<tr>
<th class="col-id" data-sort="id" onclick="sortTable('id')">
题目编号 <span class="sort-arrow">⇅</span>
</th>
<th class="col-date" data-sort="date" onclick="sortTable('date')">
日期 <span class="sort-arrow">⇅</span>
</th>
<th class="col-diff" data-sort="difficulty" onclick="sortTable('difficulty')">
难度 <span class="sort-arrow">⇅</span>
</th>
<th class="col-rating" data-sort="rating" onclick="sortTable('rating')">
评价 <span class="sort-arrow">⇅</span>
</th>
<th class="col-algo" data-sort="algo" onclick="sortTable('algo')">
算法 / 备注 <span class="sort-arrow">⇅</span>
</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
<div class="no-results" id="noResults" style="display:none;">
<span class="icon">😕</span>
没有匹配的题目记录
</div>
</div>
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast"></div>
<script>
// ==================== 数据定义 ====================
const problemData = [
{ id: 'P4562', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '数学, 组合数学, 期望' },
{ id: 'P5377', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '平面图欧拉公式, 组合数学, 排列组合' },
{ id: 'P2290', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '数学, 组合数学, 排列组合, Prüfer 序列' },
{ id: 'P2606', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '屎题', ratingRank: -1, algo: '动态规划 DP, 数学, 组合数学, Lucas 定理' },
{ id: 'P4454', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '枚举, 素数判断, 逆元, 大步小步算法 BSGS' },
{ id: 'P2485', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '屎题', ratingRank: -1, algo: '大步小步算法 BSGS, 拉完了, 勾十毒瘤题' },
{ id: 'P1298', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '大好题', ratingRank: 3, algo: 'Stern-Brocot 树' },
{ id: 'P10321', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '数学, 数论, 素数判断, 最大公约数 gcd' },
{ id: 'P4139', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '欧拉函数' },
{ id: 'P4343', date: '2025-07-15', dateDisplay: '07-15', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '二分' },
{ id: 'P4095', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '枚举, 分治, 背包 DP, 进制' },
{ id: 'P5771', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '网络流, 二分图' },
{ id: 'P3376', date: '2025-07-15', dateDisplay: '07-15', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '网络流' },
{ id: 'P3518', date: '2025-07-14', dateDisplay: '07-14', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '数学, 最大公约数 gcd' },
{ id: 'P8449', date: '2025-07-13', dateDisplay: '07-13', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '' },
{ id: 'P2851', date: '2025-07-13', dateDisplay: '07-13', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP, 背包 DP, 进制' },
{ id: 'P5749', date: '2025-07-13', dateDisplay: '07-13', difficulty: '提高', difficultyRank: 4,
rating: '大好题', ratingRank: 3, algo: '贪心, 线段树, 树状数组' },
{ id: 'P3459', date: '2025-07-13', dateDisplay: '07-13', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '线段树, 树状数组, 深度优先搜索 DFS' },
{ id: 'P3558', date: '2025-07-13', dateDisplay: '07-13', difficulty: '普及+/提高', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '动态规划 DP' },
{ id: 'P9525', date: '2025-07-13', dateDisplay: '07-13', difficulty: '提高', difficultyRank: 4,
rating: '大好题', ratingRank: 3, algo: '贪心, 优先队列' },
{ id: 'P4568', date: '2025-07-12', dateDisplay: '07-12', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '图论建模, 最短路' },
{ id: 'P3846', date: '2025-07-12', dateDisplay: '07-12', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '搜索, 数学, 数论, 素数判断, 大步小步算法 BSGS' },
{ id: 'P1197', date: '2025-07-12', dateDisplay: '07-12', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '并查集, 连通块' },
{ id: 'P2312', date: '2025-07-12', dateDisplay: '07-12', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '数学, 枚举, 哈希 hashing' },
{ id: 'P1052', date: '2025-07-12', dateDisplay: '07-12', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '动态规划 DP, 数学, 离散化, 动态规划优化, 矩阵加速, 不定方程' },
{ id: 'P3951', date: '2025-07-12', dateDisplay: '07-12', difficulty: '普及', difficultyRank: 2,
rating: '水题', ratingRank: 0, algo: '数学, 不定方程, 中国剩余定理 CRT, 构造' },
{ id: 'P1169', date: '2025-07-05', dateDisplay: '07-05', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '动态规划 DP' },
{ id: 'P2106', date: '2025-07-05', dateDisplay: '07-05', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP, 数学, 倍增, 数位 DP' },
{ id: 'P3147', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP, 贪心' },
{ id: 'P2254', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '动态规划 DP, 贪心' },
{ id: 'P2569', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP' },
{ id: 'P7469', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '字符串' },
{ id: 'P3694', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '枚举, 前缀和, 状压 DP' },
{ id: 'P3092', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '二分, 单调队列, 前缀和, 状压 DP' },
{ id: 'P6007', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP, 树状数组, 动态规划优化' },
{ id: 'P1966', date: '2025-06-30', dateDisplay: '06-30', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '树状数组, 离散化, 排序' },
{ id: 'P1073', date: '2025-06-30', dateDisplay: '06-30', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '动态规划 DP, 搜索, 图论, 最短路' },
{ id: 'P8865', date: '2025-06-30', dateDisplay: '06-30', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '数学, 枚举, 组合数学, 前缀和' },
{ id: 'P6424', date: '2025-06-28', dateDisplay: '06-28', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '数学' },
{ id: 'P4092', date: '2025-06-28', dateDisplay: '06-28', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '搜索, 线段树, 并查集, 树状数组' },
{ id: 'P4827', date: '2025-06-26', dateDisplay: '06-26', difficulty: 'NOI/NOI+/CTS', difficultyRank: 8,
rating: '难题', ratingRank: 4, algo: '动态规划 DP, 数学, 组合数学' },
{ id: 'P4827', date: '2025-06-26', dateDisplay: '06-26', difficulty: 'NOI/NOI+/CTS', difficultyRank: 8,
rating: '难题', ratingRank: 4, algo: '动态规划 DP, 数学, 组合数学' },
{ id: 'P2391', date: '2025-06-25', dateDisplay: '06-25', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '模拟, 线段树, 并查集' },
{ id: 'P3188', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '动态规划 DP, 背包 DP, 进制' },
{ id: 'P1450', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP' },
{ id: 'P5505', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '组合数学' },
{ id: 'P3761', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '枚举' },
{ id: 'P4302', date: '2025-06-23', dateDisplay: '06-23', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '字符串, 动态规划 DP, 搜索, 枚举' },
{ id: 'P5300', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '位运算, 单调栈' },
{ id: 'P3306', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '数论, 逆元, 大步小步算法 BSGS' },
{ id: 'P8251', date: '2025-06-23', dateDisplay: '06-23', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '倍增' },
{ id: 'P4609', date: '2025-06-21', dateDisplay: '06-21', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '数学' },
{ id: 'P6620', date: '2025-06-21', dateDisplay: '06-21', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '数学' },
{ id: 'P1655', date: '2025-06-12', dateDisplay: '06-12', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '动态规划 DP' },
{ id: 'P1955', date: '2025-06-10', dateDisplay: '06-10', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '并查集' },
{ id: 'P2024', date: '2025-06-10', dateDisplay: '06-10', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '并查集' },
{ id: 'P3957', date: '2025-06-07', dateDisplay: '06-07', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 D' },
{ id: 'P1641', date: '2025-06-05', dateDisplay: '06-05', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: 'Catalan 数, 逆元, Lucas 定理' },
{ id: 'P1044', date: '2025-06-05', dateDisplay: '06-05', difficulty: '普及−', difficultyRank: 1, rating: '好题',
ratingRank: 2, algo: 'Catalan 数, 栈' },
{ id: 'P2518', date: '2025-06-02', dateDisplay: '06-02', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '动态规划 DP' },
{ id: 'P10238', date: '2025-05-30', dateDisplay: '05-30', difficulty: '提高', difficultyRank: 4,
rating: '好题', ratingRank: 2, algo: '并查集' },
{ id: 'P3304', date: '2025-05-31', dateDisplay: '05-31', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '并查集' },
{ id: 'P5454', date: '2025-05-29', dateDisplay: '05-29', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '' },
{ id: 'P5367', date: '2025-05-29', dateDisplay: '05-29', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '线段树, 平衡树' },
{ id: 'P2617', date: '2025-05-27', dateDisplay: '05-27', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '莫队, 线段树' },
{ id: 'P3045', date: '2025-05-26', dateDisplay: '05-26', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '贪心, 堆, 凸完全单调性(wqs 二分)' },
{ id: 'P3527', date: '2025-05-26', dateDisplay: '05-26', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '贪心, 堆, 凸完全单调性(wqs 二分)' },
{ id: 'P3834', date: '2025-05-26', dateDisplay: '05-26', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '莫队, 线段树, 离散化, 可持久化线段树' },
{ id: 'P2624', date: '2025-05-26', dateDisplay: '05-26', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '高精度, 素数判断, Prüfer 序列' },
{ id: 'P2290', date: '2025-05-22', dateDisplay: '05-22', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '数学, 组合数学' },
{ id: 'P6086', date: '2025-05-22', dateDisplay: '05-22', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: 'Prüfer 序列' },
{ id: 'P7976', date: '2025-05-22', dateDisplay: '05-22', difficulty: '提高', difficultyRank: 4, rating: '好题',
ratingRank: 2, algo: '数学, 数位 DP, Lucas 定理' },
{ id: 'P5376', date: '2025-05-22', dateDisplay: '05-22', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '组合数学, 排列组合, 容斥原理' },
{ id: 'P6456', date: '2025-05-16', dateDisplay: '05-16', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '二分, 哈希 hashing, 后缀数组 SA' },
{ id: 'P8617', date: '2025-05-16', dateDisplay: '05-16', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '二分, 后缀自动机 SAM, 哈希 hashing' },
{ id: 'U41492', date: '2025-05-16', dateDisplay: '05-16', difficulty: '普及+/提高−', difficultyRank: 3,
rating: '好题', ratingRank: 2, algo: '树剖' },
{ id: 'P1903', date: '2025-05-16', dateDisplay: '05-16', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '莫队, cdq 分治, 树套树, 分块' },
{ id: 'P3604', date: '2025-05-16', dateDisplay: '05-16', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '莫队, 状压 D' },
{ id: 'P4867', date: '2025-05-16', dateDisplay: '05-16', difficulty: '省选/NOI−', difficultyRank: 7,
rating: '好题', ratingRank: 2, algo: '莫队, 树状数组, 分块' },
{ id: 'P4137', date: '2025-05-16', dateDisplay: '05-16', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2,
algo: '莫队, 线段树, cdq 分治, 可持久化线段树, 可持久化, 分块, 扫描线, 整体二分, bitset' },
{ id: 'P5268', date: '2025-05-16', dateDisplay: '05-16', difficulty: '提高+/省选−', difficultyRank: 5,
rating: '好题', ratingRank: 2, algo: '莫队, 容斥原理, 差分' },
];
// ==================== 状态管理 ====================
let currentSort = { field: 'date', direction: 'desc' }; // 默认按日期降序
let filteredData = [...problemData];
// ==================== 渲染表格 ====================
function getDifficultyTagClass(difficulty) {
const d = difficulty.trim();
if (d === '普及−' || d === '普及') return 'tag-diff-pup';
if (d.includes('普及+/提高') || d === '普及+/提高−' || d === '普及+/提高') return 'tag-diff-pup-plus';
if (d === '提高') return 'tag-diff-improve';
if (d === '提高+/省选−') return 'tag-diff-improve-plus';
if (d === '省选/NOI−') return 'tag-diff-prov';
if (d.includes('NOI') || d.includes('CTS')) return 'tag-diff-ct';
return 'tag-diff-improve';
}
function getRatingTagClass(rating) {
const r = rating.trim();
if (r === '大好题') return 'tag-rating-great';
if (r === '好题') return 'tag-rating-good';
if (r === '屎题') return 'tag-rating-bad';
if (r === '难题') return 'tag-rating-hard';
if (r === '水题') return 'tag-rating-water';
return 'tag-rating-good';
}
function renderTable(data) {
const tbody = document.getElementById('tableBody');
const noResults = document.getElementById('noResults');
const totalCount = document.getElementById('totalCount');
totalCount.textContent = data.length;
if (data.length === 0) {
tbody.innerHTML = '';
noResults.style.display = 'block';
return;
}
noResults.style.display = 'none';
tbody.innerHTML = data.map(item => {
const algoTags = item.algo ?
item.algo.split(/[,,]+/).map(a => a.trim()).filter(a => a).map(a =>
`<span class="algo-tag">${escapeHTML(a)}</span>`
).join('') :
'<span class="algo-tag" style="color:#94a3b8;">—</span>';
const probLink = item.id.startsWith('U') ?
escapeHTML(item.id) :
`<a href="https://www.luogu.com.cn/problem/${escapeHTML(item.id)}" target="_blank" rel="noopener" title="在洛谷打开 ${escapeHTML(item.id)}">${escapeHTML(item.id)}</a>`;
return `
<tr>
<td><span class="prob-id">${probLink}</span></td>
<td>${escapeHTML(item.dateDisplay)}</td>
<td><span class="tag ${getDifficultyTagClass(item.difficulty)}">${escapeHTML(item.difficulty)}</span></td>
<td><span class="tag ${getRatingTagClass(item.rating)}">${escapeHTML(item.rating)}</span></td>
<td><div class="algo-list">${algoTags}</div></td>
</tr>
`;
}).join('');
// 更新排序指示器
document.querySelectorAll('thead th').forEach(th => {
th.classList.remove('sorted');
if (th.dataset.sort === currentSort.field) {
th.classList.add('sorted');
const arrow = th.querySelector('.sort-arrow');
if (arrow) {
arrow.textContent = currentSort.direction === 'asc' ? '▲' : '▼';
}
} else {
const arrow = th.querySelector('.sort-arrow');
if (arrow) arrow.textContent = '⇅';
}
});
}
function escapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// ==================== 排序 ====================
function sortTable(field) {
if (currentSort.field === field) {
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
} else {
currentSort.field = field;
// 默认降序
currentSort.direction = (field === 'date') ? 'desc' : 'asc';
}
const dir = currentSort.direction === 'asc' ? 1 : -1;
filteredData.sort((a, b) => {
let valA, valB;
switch (field) {
case 'id':
valA = a.id;
valB = b.id;
return dir * valA.localeCompare(valB, undefined, { numeric: true, sensitivity: 'base' });
case 'date':
valA = a.date;
valB = b.date;
return dir * valA.localeCompare(valB);
case 'difficulty':
valA = a.difficultyRank;
valB = b.difficultyRank;
return dir * (valA - valB);
case 'rating':
valA = a.ratingRank;
valB = b.ratingRank;
return dir * (valA - valB);
case 'algo':
valA = a.algo || '';
valB = b.algo || '';
return dir * valA.localeCompare(valB, 'zh-CN');
default:
return 0;
}
});
renderTable(filteredData);
showToast(`已按「${getFieldLabel(field)}」${currentSort.direction === 'asc' ? '升序' : '降序'}排列`);
}
function getFieldLabel(field) {
const map = { id: '题目编号', date: '日期', difficulty: '难度', rating: '评价', algo: '算法备注' };
return map[field] || field;
}
// ==================== 搜索 ====================
function filterData(query) {
const q = query.toLowerCase().trim();
if (!q) {
filteredData = [...problemData];
} else {
filteredData = problemData.filter(item => {
return (
item.id.toLowerCase().includes(q) ||
item.dateDisplay.includes(q) ||
item.difficulty.toLowerCase().includes(q) ||
item.rating.toLowerCase().includes(q) ||
(item.algo || '').toLowerCase().includes(q)
);
});
}
// 重新应用排序
const dir = currentSort.direction === 'asc' ? 1 : -1;
const field = currentSort.field;
filteredData.sort((a, b) => {
let valA, valB;
switch (field) {
case 'id':
return dir * a.id.localeCompare(b.id, undefined, { numeric: true, sensitivity: 'base' });
case 'date':
return dir * a.date.localeCompare(b.date);
case 'difficulty':
return dir * (a.difficultyRank - b.difficultyRank);
case 'rating':
return dir * (a.ratingRank - b.ratingRank);
case 'algo':
return dir * ((a.algo || '').localeCompare(b.algo || '', 'zh-CN'));
default:
return 0;
}
});
renderTable(filteredData);
}
function resetTable() {
document.getElementById('searchInput').value = '';
currentSort = { field: 'date', direction: 'desc' };
filteredData = [...problemData];
filteredData.sort((a, b) => b.date.localeCompare(a.date));
renderTable(filteredData);
showToast('已重置搜索与排序');
}
// ==================== CSV下载 ====================
function downloadCSV() {
const dataToExport = filteredData.length > 0 ? filteredData : problemData;
const BOM = '\uFEFF';
const header = '题目编号,日期,难度,评价,算法/备注\n';
const rows = dataToExport.map(item => {
const algo = item.algo || '';
// CSV转义:包含逗号、双引号或换行符的字段用双引号包裹
const escapeCSV = (str) => {
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes(',')) {
return '"' + str.replace(/"/g, '""') + '"';
}
return str;
};
return [escapeCSV(item.id), escapeCSV(item.dateDisplay), escapeCSV(item.difficulty),
escapeCSV(item.rating), escapeCSV(algo)
].join(',');
}).join('\n');
const csvContent = BOM + header + rows;
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
const now = new Date();
const dateStr = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}`;
link.download = `算法刷题记录_${dateStr}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
showToast('✅ CSV文件下载成功!可用Excel / WPS / Google Sheets打开');
}
// ==================== Toast ====================
let toastTimer;
function showToast(message) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toast.classList.remove('show');
}, 2000);
}
// ==================== 初始化 ====================
function init() {
// 默认按日期降序排列
filteredData = [...problemData];
filteredData.sort((a, b) => b.date.localeCompare(a.date));
currentSort = { field: 'date', direction: 'desc' };
renderTable(filteredData);
// 搜索事件监听
const searchInput = document.getElementById('searchInput');
searchInput.addEventListener('input', (e) => {
filterData(e.target.value);
});
// 也支持回车搜索
searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
resetTable();
}
});
// 更新总计数
document.getElementById('totalCount').textContent = problemData.length;
console.log(`✅ 算法刷题记录已加载,共 ${problemData.length} 条记录`);
console.log('💡 提示:点击表头可排序 | 搜索框可筛选 | 点击"下载CSV"导出表格');
}
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>