洛谷题单一键转 vjudge 题单

可以一键复制 vjudge 格式的题单到剪切板,注意到先点到题单的 #problems 页。

// ==UserScript==
// @name         洛谷题单转 VJudge 题单
// @namespace    https://luogu-to-vjudge
// @version      0.2
// @description  将洛谷题单一键导出为 VJudge 可导入的题单格式
// @author       ChatGPT & jianhe
// @match        https://www.luogu.com.cn/problem/list*
// @match        https://www.luogu.com.cn/training/*
// @grant        GM_setClipboard
// ==/UserScript==

(function(){
    'use strict';

    function extractProblems(){
        const res=[];
        document.querySelectorAll('a[href^="/problem/"]').forEach(a=>{
            const href=a.getAttribute('href');
            let m;
            if(m=href.match(/\/problem\/(P\d+)/))
                res.push({oj:'洛谷',id:m[1]});
            else if(m=href.match(/\/problem\/(CF[0-9A-Z]+)/))
                res.push({oj:'CodeForces',id:m[1].slice(2)});
            else if(m=href.match(/\/problem\/(AT_[\w]+)/))
                res.push({oj:'AtCoder',id:m[1].slice(3)});
            else if(m=href.match(/\/problem\/(UVA[0-9]+)/))
                res.push({oj:'UVA',id:m[1].slice(3)});
            else if(m=href.match(/\/problem\/(SP\d+)/)){
                // SPOJ: 需要取题目标题的第一个词
                const title = a.textContent.trim();
                const firstWord = title.split(/\s+/)[0];
                if(firstWord){
                    res.push({oj:'SPOJ',id:firstWord});
                }
            }
        });
        const mp=new Map();
        res.forEach(p=>{
            const k=p.oj+'-'+p.id;
            if(!mp.has(k))mp.set(k,p);
        });
        return [...mp.values()];
    }

    function toVJudge(list){
        return list.map(p=>`[problem:${p.oj}-${p.id}]`).join('\n');
    }

    function addButton(){
        const btn=document.createElement('button');
        btn.textContent='导出到 VJudge';
        btn.style.cssText='position:fixed;right:20px;bottom:20px;z-index:9999;padding:8px 12px;border-radius:6px;background:#2d8cf0;color:#fff;border:none;cursor:pointer;';
        btn.onclick=()=>{
            const probs=extractProblems();
            if(!probs.length){alert('未检测到题目');return;}
            const txt=toVJudge(probs);
            GM_setClipboard(txt);
            alert('已复制到剪贴板,可直接粘贴到 VJudge 题单');
        };
        document.body.appendChild(btn);
    }

    window.addEventListener('load',addButton);
})();

posted @ 2025-12-18 19:17  见合  阅读(16)  评论(0)    收藏  举报