AtCoderで入力値の制約が極端な時に目立たせる。(inspired by Time Limit Emphasizer by https://github.com/Ogtsn99)
// ==UserScript==
// @name Constraints Emphasizer
// @namespace https://github.com/TrueRyoB
// @version 1.3
// @description AtCoderで入力値の制約が極端な時に目立たせる。(inspired by Time Limit Emphasizer by https://github.com/Ogtsn99)
// @include https://atcoder.jp/contests/*/tasks/*
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @author TrueRyoB
// @license MIT
// ==/UserScript==
const styles = [
{ ub: 10, className: 'dneg-perm' },
{ ub: 20, className: 'dneg-2pn' },
{ ub: 30, className: 'dneg-2pmdiv3' },
{ ub: 100, className: 'dneg-np4' },
{ ub: 300, className: 'dneg-np3' },
{ ub: 1000, className: 'dneg-np2' },
{ ub: 1000000, className: 'dneg-nlogn' },
{ ub: 100000000, className: 'dneg-n' },
{ ub: 1000000000000, className: 'dneg-logn' },
];
$('<style>')
.prop('type', 'text/css')
.html(`
.dneg-c { color:#e2345; font-weight:1000; }
.dneg-perm { color:#492ed1; font-weight:700; }
.dneg-2pn { color:#492ed1; font-weight:700; }
.dneg-2pmdiv3 { color:#492ed1; font-weight:700; }
.dneg-np4 { color:#2ed192; font-weight:700; }
.dneg-np3 { color:#2ed192; font-weight:700; }
.dneg-np2 { color:#2ed192; font-weight:700; }
.dneg-nlogn { }
.dneg-n { }
.dneg-logn { color: #e23454; font-weight:700;}
`)
.appendTo('head');
const $container = $('section h3:contains("制約")');
$container.next('ul').find('li').each(function () {
const annotation = $(this).find('annotation[encoding="application/x-tex"]').text();
console.log("new annotation:", annotation);
const bound = extractMinMax(annotation);
let found=false;
for (const style of styles) {
if(style.ub <= bound.max) {
$(this).addClass(style.className);found=true;break;
}
}
if(!found) $(this).addClass('dneg-c');
});
function extractMinMax(annotation) {
const match = annotation.match(/(\d+)\s*\\leq\s*.+?\s*\\leq\s*(.+)/);
if (!match) return null;
const min = parseInt(match[1], 10);
const rawMax = match[2].trim();
let max;
const funcMatch = rawMax.match(/\\(min|max)\s*\(([^)]+)\)/);
if (funcMatch) {
const [, funcType, args] = funcMatch;
const values = args.split(',').map(s => s.trim());
let numericValues = values.map(val => {
const expMatch = val.match(/(\d+)\s*\\times\s*10\^\{?(\d+)\}?/);
if (expMatch) {
return parseInt(expMatch[1], 10) * Math.pow(10, parseInt(expMatch[2], 10));
}
const plainNum = val.match(/^\d+$/);
return plainNum ? parseInt(val, 10) : null;
}).filter(v => v !== null);
if (numericValues.length === 0) return null;
max = funcType === "min"
? Math.min(...numericValues)
: Math.max(...numericValues);
} else {
const expMatch = rawMax.match(/(\d+)\s*\\times\s*10\^\{?(\d+)\}?/);
if (expMatch) {
max = parseInt(expMatch[1], 10) * Math.pow(10, parseInt(expMatch[2], 10));
} else {
const plainNum = rawMax.match(/^\d+$/);
max = plainNum ? parseInt(rawMax, 10) : null;
}
}
if (isNaN(min) || isNaN(max)) return null;
return { min, max };
}