2022-11-06 02:26:21 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
// this class depends on getUOJConf from uoj-judger-lib.php sometimes
|
|
|
|
|
// be sure to include the lib
|
|
|
|
|
// TODO: move getUOJConf into a static class independent of uoj-judger-lib.php
|
|
|
|
|
|
|
|
|
|
class UOJProblem {
|
|
|
|
|
use UOJDataTrait;
|
|
|
|
|
use UOJArticleTrait;
|
|
|
|
|
|
2022-12-04 09:27:16 +00:00
|
|
|
|
public static array $difficulty = [
|
2022-12-05 09:14:43 +00:00
|
|
|
|
800,
|
|
|
|
|
1000,
|
|
|
|
|
1200,
|
|
|
|
|
1400,
|
|
|
|
|
1600,
|
|
|
|
|
1800,
|
|
|
|
|
1900,
|
|
|
|
|
2000,
|
|
|
|
|
2100,
|
|
|
|
|
2200,
|
|
|
|
|
2300,
|
|
|
|
|
2400,
|
|
|
|
|
2500,
|
2022-12-08 05:04:44 +00:00
|
|
|
|
2600,
|
2022-12-05 09:14:43 +00:00
|
|
|
|
2700,
|
|
|
|
|
2900,
|
|
|
|
|
3100,
|
|
|
|
|
3300,
|
|
|
|
|
3500,
|
2022-12-04 09:27:16 +00:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public static array $difficulty_color = [
|
2022-12-05 09:14:43 +00:00
|
|
|
|
800 => '#008000',
|
|
|
|
|
1000 => '#008000',
|
|
|
|
|
1200 => '#00c0c0',
|
|
|
|
|
1400 => '#00c0c0',
|
|
|
|
|
1600 => '#0000ff',
|
|
|
|
|
1800 => '#0000ff',
|
|
|
|
|
1900 => '#0000ff',
|
2022-12-05 11:00:23 +00:00
|
|
|
|
2000 => '#aa00aa',
|
|
|
|
|
2100 => '#aa00aa',
|
|
|
|
|
2200 => '#aa00aa',
|
|
|
|
|
2300 => '#ff8000',
|
2022-12-05 09:14:43 +00:00
|
|
|
|
2400 => '#ff8000',
|
|
|
|
|
2500 => '#ff8000',
|
2022-12-08 05:04:44 +00:00
|
|
|
|
2600 => '#ff0000',
|
2022-12-05 11:00:23 +00:00
|
|
|
|
2700 => '#ff0000',
|
2022-12-05 09:14:43 +00:00
|
|
|
|
2900 => '#ff0000',
|
2022-12-08 05:04:44 +00:00
|
|
|
|
3100 => '#aa0000',
|
2022-12-05 09:14:43 +00:00
|
|
|
|
3300 => '#aa0000',
|
|
|
|
|
3500 => '#aa0000',
|
2022-12-04 09:27:16 +00:00
|
|
|
|
];
|
|
|
|
|
|
2023-01-12 02:06:12 +00:00
|
|
|
|
public static array $categories = [
|
|
|
|
|
'算法基础' => [
|
|
|
|
|
'暴力',
|
|
|
|
|
'枚举',
|
|
|
|
|
'模拟',
|
|
|
|
|
'递归与分治',
|
|
|
|
|
'贪心',
|
|
|
|
|
'排序',
|
|
|
|
|
'前缀和与差分',
|
|
|
|
|
'二分',
|
|
|
|
|
'倍增',
|
|
|
|
|
'构造',
|
|
|
|
|
'打表',
|
|
|
|
|
],
|
|
|
|
|
'搜索' => [
|
|
|
|
|
'深度优先搜索',
|
|
|
|
|
'广度优先搜索',
|
|
|
|
|
'双向搜索',
|
|
|
|
|
'启发式搜索',
|
|
|
|
|
'A*',
|
|
|
|
|
'IDA*',
|
|
|
|
|
'迭代加深',
|
|
|
|
|
'回溯法',
|
|
|
|
|
'Dancing Links',
|
|
|
|
|
],
|
|
|
|
|
'动态规划' => [
|
|
|
|
|
'记忆化搜索',
|
|
|
|
|
'线性 DP',
|
|
|
|
|
'背包 DP',
|
|
|
|
|
'区间 DP',
|
|
|
|
|
'树形 DP',
|
|
|
|
|
'状压 DP',
|
|
|
|
|
'数位 DP',
|
|
|
|
|
'DAG 上 DP',
|
|
|
|
|
'插头 DP',
|
|
|
|
|
'概率 DP',
|
|
|
|
|
'单调队列优化 DP',
|
|
|
|
|
'斜率优化 DP',
|
|
|
|
|
'四边形不等式优化 DP',
|
|
|
|
|
],
|
|
|
|
|
'计算几何' => [
|
|
|
|
|
'Pick 定理',
|
|
|
|
|
'三角剖分',
|
|
|
|
|
'凸包',
|
|
|
|
|
'扫描线',
|
|
|
|
|
'旋转卡壳',
|
|
|
|
|
'半平面交',
|
|
|
|
|
'平面最近点对',
|
|
|
|
|
'随机增量法',
|
|
|
|
|
'反演变换',
|
|
|
|
|
],
|
|
|
|
|
'数学' => [
|
|
|
|
|
'位运算',
|
|
|
|
|
'快速幂',
|
|
|
|
|
'高精度',
|
|
|
|
|
'生成函数',
|
|
|
|
|
'指数生成函数',
|
|
|
|
|
'向量',
|
|
|
|
|
'矩阵',
|
|
|
|
|
'高斯消元',
|
|
|
|
|
'线性基',
|
|
|
|
|
'线性规划',
|
|
|
|
|
'容斥',
|
|
|
|
|
'组合计数',
|
|
|
|
|
'离散对数',
|
|
|
|
|
'单纯形算法',
|
|
|
|
|
'概率',
|
|
|
|
|
'置换群',
|
|
|
|
|
'斐波那契数列',
|
|
|
|
|
'牛顿迭代法',
|
|
|
|
|
'数值积分',
|
|
|
|
|
'分块打表',
|
|
|
|
|
],
|
|
|
|
|
'数论' => [
|
|
|
|
|
'最大公约数',
|
|
|
|
|
'分解质因数',
|
|
|
|
|
'欧拉函数',
|
|
|
|
|
'筛法',
|
|
|
|
|
'欧拉定理',
|
|
|
|
|
'费马小定理',
|
|
|
|
|
'类欧几里得算法',
|
|
|
|
|
'翡蜀定理',
|
|
|
|
|
'乘法逆元',
|
|
|
|
|
'线性同余方程',
|
|
|
|
|
'Meissel-Lehmer 算法',
|
|
|
|
|
'二次剩余',
|
|
|
|
|
'BSGS',
|
|
|
|
|
'原根',
|
|
|
|
|
'卢卡斯定理',
|
|
|
|
|
'莫比乌斯反演',
|
|
|
|
|
'拉格朗日反演',
|
|
|
|
|
'杜教筛',
|
|
|
|
|
'Powerful Number 筛',
|
|
|
|
|
'Min_25 筛',
|
|
|
|
|
'洲阁筛',
|
|
|
|
|
'连分数',
|
|
|
|
|
'Stern-Brocot 数与 Farey 序列',
|
|
|
|
|
'Pell 方程',
|
|
|
|
|
],
|
|
|
|
|
'字符串' => [
|
|
|
|
|
'字符串哈希',
|
|
|
|
|
'字典树',
|
|
|
|
|
'KMP',
|
|
|
|
|
'Boyer-Moore',
|
|
|
|
|
'Z 函数(扩展 KMP)',
|
|
|
|
|
'AC 自动机',
|
|
|
|
|
'后缀数组',
|
|
|
|
|
'后缀自动机',
|
|
|
|
|
'后缀平衡树',
|
|
|
|
|
'广义后缀自动机',
|
|
|
|
|
'Manacher',
|
|
|
|
|
'回文树',
|
|
|
|
|
'序列自动机',
|
|
|
|
|
'最小表示法',
|
|
|
|
|
'Lyndon 分解',
|
|
|
|
|
],
|
|
|
|
|
'图论' => [
|
|
|
|
|
'拓扑排序',
|
|
|
|
|
'最短路',
|
|
|
|
|
'K 短路',
|
|
|
|
|
'同余最短路',
|
|
|
|
|
'虚树',
|
|
|
|
|
'树分治',
|
|
|
|
|
'动态树分治',
|
|
|
|
|
'树哈希',
|
|
|
|
|
'树上启发式合并',
|
|
|
|
|
'AHU 算法',
|
|
|
|
|
'矩阵树定理',
|
|
|
|
|
'最小生成树',
|
|
|
|
|
'最小树形图',
|
|
|
|
|
'最小直径生成树',
|
|
|
|
|
'斯坦纳树',
|
|
|
|
|
'拆点',
|
|
|
|
|
'差分约束',
|
|
|
|
|
'强连通分量',
|
|
|
|
|
'双连通分量',
|
|
|
|
|
'割点与桥',
|
|
|
|
|
'圆方树',
|
|
|
|
|
'2-SAT',
|
|
|
|
|
'欧拉图',
|
|
|
|
|
'哈密顿图',
|
|
|
|
|
'最小环',
|
|
|
|
|
'平面图',
|
|
|
|
|
'网络流',
|
|
|
|
|
'最大流',
|
|
|
|
|
'最小割',
|
|
|
|
|
'费用流',
|
|
|
|
|
'上下界网络流',
|
|
|
|
|
'Stoer-Wagner 算法',
|
|
|
|
|
'二分图',
|
|
|
|
|
'二分图最大匹配',
|
|
|
|
|
'二分图最大权匹配',
|
|
|
|
|
'一般图最大匹配',
|
|
|
|
|
'一般图最大权匹配',
|
|
|
|
|
'Prufer 序列',
|
|
|
|
|
'LGV 引理',
|
|
|
|
|
'弦图',
|
|
|
|
|
],
|
|
|
|
|
'组合数学' => [
|
|
|
|
|
'排列组合',
|
|
|
|
|
'卡特兰数',
|
|
|
|
|
'斯特林数',
|
|
|
|
|
'贝尔数',
|
|
|
|
|
'伯努利数',
|
|
|
|
|
'康托展开',
|
|
|
|
|
'容斥原理',
|
|
|
|
|
'抽屉原理',
|
|
|
|
|
'欧拉数',
|
|
|
|
|
],
|
|
|
|
|
'数据结构' => [
|
|
|
|
|
'栈',
|
|
|
|
|
'队列',
|
|
|
|
|
'链表',
|
|
|
|
|
'哈希表',
|
|
|
|
|
'并查集',
|
|
|
|
|
'二叉堆',
|
|
|
|
|
'配对堆',
|
|
|
|
|
'树状数组',
|
|
|
|
|
'线段树',
|
|
|
|
|
'平衡树',
|
|
|
|
|
'左偏树',
|
|
|
|
|
'块状数组',
|
|
|
|
|
'块状链表',
|
|
|
|
|
'树分块',
|
|
|
|
|
'Sqrt Tree',
|
|
|
|
|
'可持久化数据结构',
|
|
|
|
|
'单调栈',
|
|
|
|
|
'单调队列',
|
|
|
|
|
'ST 表',
|
|
|
|
|
'树套树',
|
|
|
|
|
'李超线段树',
|
|
|
|
|
'区间最值操作与区间历史最值',
|
|
|
|
|
'划分树',
|
|
|
|
|
'跳表',
|
|
|
|
|
'K-D Tree',
|
|
|
|
|
'珂朵莉树',
|
|
|
|
|
'动态树',
|
|
|
|
|
'析合树',
|
|
|
|
|
],
|
|
|
|
|
'多项式' => [
|
|
|
|
|
'拉格朗日插值',
|
|
|
|
|
'快速傅里叶变换',
|
|
|
|
|
'快速数论变换',
|
|
|
|
|
'快速沃尔什变换',
|
|
|
|
|
'多项式求逆',
|
|
|
|
|
'多项式开方',
|
|
|
|
|
'多项式除法与取模',
|
|
|
|
|
'多项式对数函数与指数函数',
|
|
|
|
|
'多项式牛顿迭代',
|
|
|
|
|
'多项式多点求值与快速插值',
|
|
|
|
|
'多项式三角函数',
|
|
|
|
|
'多项式反三角函数',
|
|
|
|
|
'常系数齐次线性递推',
|
|
|
|
|
],
|
|
|
|
|
'博弈论' => [
|
|
|
|
|
'不平等博弈',
|
|
|
|
|
'SG 函数',
|
|
|
|
|
'Nim 游戏',
|
|
|
|
|
'Anti-Nim',
|
|
|
|
|
'纳什均衡',
|
|
|
|
|
],
|
|
|
|
|
'杂项' => [
|
|
|
|
|
'构造',
|
|
|
|
|
'离散化',
|
|
|
|
|
'CDQ 分治',
|
|
|
|
|
'整体二分',
|
|
|
|
|
'分块',
|
|
|
|
|
'莫队',
|
|
|
|
|
'分数规划',
|
|
|
|
|
'随机化',
|
|
|
|
|
'模拟退火',
|
|
|
|
|
'爬山法',
|
|
|
|
|
'悬线法',
|
|
|
|
|
'编译原理',
|
|
|
|
|
'复杂度分析',
|
|
|
|
|
'语义分析',
|
|
|
|
|
'底层优化',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
public static function query($id) {
|
|
|
|
|
if (!isset($id) || !validateUInt($id)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
$info = DB::selectFirst([
|
|
|
|
|
"select * from problems",
|
|
|
|
|
"where", ['id' => $id]
|
|
|
|
|
]);
|
|
|
|
|
if (!$info) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return new UOJProblem($info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function upgradeToContestProblem() {
|
|
|
|
|
return (new UOJContestProblem(self::cur()->info, UOJContest::cur()))->setAsCur()->valid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function userCanManageSomeProblem(array $user = null) {
|
|
|
|
|
if (!$user) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-11 00:20:33 +00:00
|
|
|
|
|
2022-11-11 23:10:34 +00:00
|
|
|
|
if (isSuperUser($user) || UOJUser::checkPermission($user, 'problems.manage')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return DB::selectFirst([
|
|
|
|
|
DB::lc(), "select 1 from problems_permissions",
|
|
|
|
|
"where", [
|
|
|
|
|
'username' => $user['username']
|
|
|
|
|
], DB::limit(1)
|
2022-11-11 00:20:33 +00:00
|
|
|
|
]) != null || DB::selectFirst([
|
|
|
|
|
DB::lc(), "select 1 from problems",
|
|
|
|
|
"where", [
|
|
|
|
|
"uploader" => $user['username'],
|
|
|
|
|
], DB::limit(1),
|
2022-11-06 02:26:21 +00:00
|
|
|
|
]) != null;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-11 23:10:34 +00:00
|
|
|
|
public static function userCanCreateProblem(array $user = null) {
|
|
|
|
|
if (!$user) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return isSuperUser($user) || UOJUser::checkPermission($user, 'problems.create');
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
public function __construct($info) {
|
|
|
|
|
$this->info = $info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getTitle(array $cfg = []) {
|
|
|
|
|
$cfg += [
|
|
|
|
|
'with' => 'id',
|
|
|
|
|
'simplify' => false
|
|
|
|
|
];
|
|
|
|
|
$title = $this->info['title'];
|
|
|
|
|
if ($cfg['simplify']) {
|
|
|
|
|
$title = trim($title);
|
|
|
|
|
$title = mb_ereg_replace('^(\[[^\]]*\]|【[^】]*】)', '', $title);
|
|
|
|
|
$title = trim($title);
|
|
|
|
|
}
|
|
|
|
|
if ($cfg['with'] == 'id') {
|
|
|
|
|
return "#{$this->info['id']}. {$title}";
|
|
|
|
|
} else {
|
|
|
|
|
return $title;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getUri($where = '') {
|
|
|
|
|
return "/problem/{$this->info['id']}{$where}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getLink(array $cfg = []) {
|
2023-02-13 03:57:10 +00:00
|
|
|
|
return HTML::link($this->getUri(), $this->getTitle($cfg), ['escape' => false, 'class' => $cfg['class'] ?: '']);
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getAttachmentUri() {
|
|
|
|
|
return '/download/problem/' . $this->info['id'] . '/attachment.zip';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getMainDataUri() {
|
|
|
|
|
return '/download/problem/' . $this->info['id'] . '/data.zip';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getUploaderLink() {
|
2022-11-11 23:11:49 +00:00
|
|
|
|
return UOJUser::getLink($this->info['uploader'] ?: "root");
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-18 08:20:12 +00:00
|
|
|
|
public function getProviderLink() {
|
2023-01-19 08:06:42 +00:00
|
|
|
|
if ($this->info['type'] == 'local') {
|
2023-01-18 08:20:12 +00:00
|
|
|
|
return HTML::tag('a', ['href' => HTML::url('/')], UOJConfig::$data['profile']['oj-name-short']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$remote_oj = $this->getExtraConfig('remote_online_judge');
|
2023-01-19 03:49:15 +00:00
|
|
|
|
$remote_id = $this->getExtraConfig('remote_problem_id');
|
2023-01-18 08:20:12 +00:00
|
|
|
|
|
|
|
|
|
if (!$remote_oj || !array_key_exists($remote_oj, UOJRemoteProblem::$providers)) {
|
|
|
|
|
return 'Error';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$provider = UOJRemoteProblem::$providers[$remote_oj];
|
|
|
|
|
|
2023-01-19 03:49:15 +00:00
|
|
|
|
return HTML::tag('a', [
|
|
|
|
|
'href' => UOJRemoteProblem::getProblemRemoteUrl($remote_oj, $remote_id),
|
|
|
|
|
'target' => '_blank'
|
|
|
|
|
], $provider['name']);
|
2023-01-18 08:20:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-05 09:14:43 +00:00
|
|
|
|
public function getDifficultyHTML() {
|
|
|
|
|
$difficulty = (int)$this->info['difficulty'];
|
2022-12-05 09:46:40 +00:00
|
|
|
|
$difficulty_text = in_array($difficulty, static::$difficulty) ? $difficulty : '?';
|
2022-12-05 09:14:43 +00:00
|
|
|
|
$difficulty_color = in_array($difficulty, static::$difficulty) ? static::$difficulty_color[$difficulty] : '#7e7e7e';
|
|
|
|
|
|
|
|
|
|
return HTML::tag('span', ['class' => 'uoj-difficulty', 'style' => "color: $difficulty_color"], $difficulty_text);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
public function findInContests() {
|
|
|
|
|
$res = DB::selectAll([
|
|
|
|
|
"select contest_id from contests_problems",
|
|
|
|
|
"where", ['problem_id' => $this->info['id']]
|
|
|
|
|
]);
|
|
|
|
|
$cps = [];
|
|
|
|
|
foreach ($res as $row) {
|
|
|
|
|
$cp = new UOJContestProblem($this->info, UOJContest::query($row['contest_id']));
|
|
|
|
|
if ($cp->valid()) {
|
|
|
|
|
$cps[] = $cp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $cps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userCanClickZan(array $user = null) {
|
|
|
|
|
if ($this->userCanView($user)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
foreach ($this->findInContests() as $cp) {
|
|
|
|
|
if ($cp->userCanClickZan($user)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 13:37:37 +00:00
|
|
|
|
public function getZanBlock() {
|
|
|
|
|
return ClickZans::getBlock('P', $this->info['id'], $this->info['zan']);
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getSubmissionRequirement() {
|
|
|
|
|
return json_decode($this->info['submission_requirement'], true);
|
|
|
|
|
}
|
|
|
|
|
public function getExtraConfig($key = null) {
|
|
|
|
|
$extra_config = json_decode($this->info['extra_config'], true);
|
|
|
|
|
|
|
|
|
|
$extra_config += [
|
|
|
|
|
'view_content_type' => 'ALL',
|
|
|
|
|
'view_all_details_type' => 'ALL',
|
|
|
|
|
'view_details_type' => 'ALL',
|
|
|
|
|
'view_solution_type' => 'ALL',
|
|
|
|
|
'submit_solution_type' => 'ALL_AFTER_AC',
|
|
|
|
|
'need_to_review_hack' => false,
|
|
|
|
|
'add_hack_as' => 'ex_test',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $key === null ? $extra_config : $extra_config[$key];
|
|
|
|
|
}
|
|
|
|
|
public function getCustomTestRequirement() {
|
|
|
|
|
$extra_config = json_decode($this->info['extra_config'], true);
|
2023-01-19 07:40:46 +00:00
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
if (isset($extra_config['custom_test_requirement'])) {
|
|
|
|
|
return $extra_config['custom_test_requirement'];
|
2023-01-19 07:40:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$answer = [
|
|
|
|
|
'name' => 'answer',
|
|
|
|
|
'type' => 'source code',
|
|
|
|
|
'file_name' => 'answer.code'
|
|
|
|
|
];
|
|
|
|
|
foreach ($this->getSubmissionRequirement() as $req) {
|
|
|
|
|
if ($req['name'] == 'answer' && $req['type'] == 'source code' && isset($req['languages'])) {
|
|
|
|
|
$answer['languages'] = $req['languages'];
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-19 07:40:46 +00:00
|
|
|
|
return [
|
|
|
|
|
$answer, [
|
|
|
|
|
'name' => 'input',
|
|
|
|
|
'type' => 'text',
|
|
|
|
|
'file_name' => 'input.txt'
|
|
|
|
|
]
|
|
|
|
|
];
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userCanView(array $user = null, array $cfg = []) {
|
|
|
|
|
$cfg += ['ensure' => false];
|
2022-11-11 23:10:34 +00:00
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
if ($this->info['is_hidden'] && !$this->userCanManage($user)) {
|
|
|
|
|
$cfg['ensure'] && UOJResponse::page404();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-11 23:10:34 +00:00
|
|
|
|
|
|
|
|
|
if (!UOJUser::checkPermission($user, 'problems.view')) {
|
|
|
|
|
$cfg['ensure'] && UOJResponse::page403();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a SQL cause to determine whether a user can view a problem
|
|
|
|
|
* Need to be consistent with the member function userCanView
|
|
|
|
|
*/
|
|
|
|
|
public static function sqlForUserCanView(array $user = null) {
|
2022-11-11 23:10:34 +00:00
|
|
|
|
if (isSuperUser($user) || UOJUser::checkPermission($user, 'problems.manage')) {
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return "(1)";
|
|
|
|
|
} elseif (UOJProblem::userCanManageSomeProblem($user)) {
|
|
|
|
|
return DB::lor([
|
|
|
|
|
"problems.is_hidden" => false,
|
|
|
|
|
DB::land([
|
|
|
|
|
"problems.is_hidden" => true,
|
|
|
|
|
DB::lor([
|
|
|
|
|
[
|
|
|
|
|
"problems.id", "in", DB::rawbracket([
|
|
|
|
|
"select problem_id from problems_permissions",
|
|
|
|
|
"where", ["username" => $user['username']]
|
|
|
|
|
])
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
"problems.id", "in", DB::rawbracket([
|
|
|
|
|
"select problem_id from problems",
|
|
|
|
|
"where", ["uploader" => $user['username']]
|
|
|
|
|
])
|
|
|
|
|
],
|
|
|
|
|
])
|
|
|
|
|
])
|
|
|
|
|
]);
|
|
|
|
|
} else {
|
|
|
|
|
return "(problems.is_hidden = false)";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function isUserOwnProblem(array $user = null) {
|
|
|
|
|
if (!$user) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $user['username'] === $this->info['uploader'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userPermissionCodeCheck(array $user = null, $perm_code) {
|
|
|
|
|
switch ($perm_code) {
|
|
|
|
|
case 'ALL':
|
|
|
|
|
return true;
|
|
|
|
|
case 'ALL_AFTER_AC':
|
|
|
|
|
return $this->userHasAC($user);
|
|
|
|
|
case 'NONE':
|
|
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userCanUploadSubmissionViaZip(array $user = null) {
|
2023-02-01 12:27:05 +00:00
|
|
|
|
$submission_requirement = $this->getSubmissionRequirement();
|
|
|
|
|
|
|
|
|
|
if (empty($submission_requirement)) return false;
|
|
|
|
|
|
|
|
|
|
foreach ($submission_requirement as $req) {
|
2022-11-06 02:26:21 +00:00
|
|
|
|
if ($req['type'] == 'source code') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-01 12:27:05 +00:00
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userCanDownloadAttachments(array $user = null) {
|
|
|
|
|
if ($this->userCanView($user)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
foreach ($this->findInContests() as $cp) {
|
|
|
|
|
if ($cp->userCanDownloadAttachments($user)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userCanManage(array $user = null) {
|
|
|
|
|
if (!$user) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-11 23:10:34 +00:00
|
|
|
|
|
|
|
|
|
if (isSuperUser($user) || $this->isUserOwnProblem($user) || UOJUser::checkPermission($user, 'problems.manage')) {
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-11-11 23:10:34 +00:00
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return DB::selectFirst([
|
|
|
|
|
DB::lc(), "select 1 from problems_permissions",
|
|
|
|
|
"where", [
|
|
|
|
|
'username' => $user['username'],
|
|
|
|
|
'problem_id' => $this->info['id']
|
|
|
|
|
]
|
|
|
|
|
]) != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userCanDownloadTestData(array $user = null) {
|
2022-11-12 01:59:15 +00:00
|
|
|
|
if ($this->userCanManage($user)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!UOJUser::checkPermission($user, 'problems.download_testdata')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-12 02:25:46 +00:00
|
|
|
|
foreach ($this->findInContests() as $cp) {
|
|
|
|
|
if ($cp->contest->userHasRegistered($user) && $cp->contest->progress() == CONTEST_IN_PROGRESS) {
|
2022-11-12 01:59:15 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function preHackCheck(array $user = null) {
|
|
|
|
|
return $this->info['hackable'] && (!$user || $this->userCanView($user));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function needToReviewHack() {
|
|
|
|
|
return $this->getExtraConfig('need_to_review_hack');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userHasAC(array $user = null) {
|
|
|
|
|
if (!$user) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return DB::selectFirst([
|
|
|
|
|
DB::lc(), "select 1 from best_ac_submissions",
|
|
|
|
|
"where", [
|
|
|
|
|
'submitter' => $user['username'],
|
|
|
|
|
'problem_id' => $this->info['id']
|
|
|
|
|
]
|
|
|
|
|
]) != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function preSubmitCheck() {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function additionalSubmissionComponentsCannotBeSeenByUser(array $user = null, UOJSubmission $submission) {
|
2022-11-12 02:25:46 +00:00
|
|
|
|
foreach ($this->findInContests() as $cp) {
|
|
|
|
|
if ($cp->contest->userHasRegistered($user) && $cp->contest->progress() == CONTEST_IN_PROGRESS) {
|
2022-11-12 02:20:28 +00:00
|
|
|
|
if ($submission->userIsSubmitter($user)) {
|
2023-02-07 11:43:24 +00:00
|
|
|
|
if ($cp->getJudgeTypeInContest() == 'no-details') {
|
2022-11-12 02:20:28 +00:00
|
|
|
|
return ['low_level_details'];
|
|
|
|
|
} else {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return ['content', 'high_level_details', 'low_level_details'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getDataFolderPath() {
|
|
|
|
|
return "/var/uoj_data/{$this->info['id']}";
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-04 13:52:59 +00:00
|
|
|
|
public function getUploadFolderPath() {
|
|
|
|
|
return "/var/uoj_data/upload/{$this->info['id']}";
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
public function getDataZipPath() {
|
|
|
|
|
return "/var/uoj_data/{$this->info['id']}.zip";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getDataFilePath($name = '') {
|
|
|
|
|
// return "zip://{$this->getDataZipPath()}#{$this->info['id']}/$name";
|
|
|
|
|
return "{$this->getDataFolderPath()}/$name";
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-04 13:52:59 +00:00
|
|
|
|
public function getUploadFilePath($name = '') {
|
|
|
|
|
return "{$this->getUploadFolderPath()}/$name";
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 10:47:44 +00:00
|
|
|
|
public function getResourcesFolderPath() {
|
|
|
|
|
return UOJContext::storagePath() . "/problem_resources/" . $this->info['id'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getResourcesPath($name = '') {
|
|
|
|
|
return "{$this->getResourcesFolderPath()}/$name";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getResourcesBaseUri() {
|
|
|
|
|
return "/problem/{$this->info['id']}/resources";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getResourcesUri($name = '') {
|
|
|
|
|
return "{$this->getResourcesBaseUri()}/{$name}";
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-06 02:26:21 +00:00
|
|
|
|
public function getProblemConfArray(string $where = 'data') {
|
|
|
|
|
if ($where === 'data') {
|
2023-02-01 11:36:27 +00:00
|
|
|
|
$conf = UOJProblemConf::getFromFile($this->getDataFilePath('problem.conf'));
|
|
|
|
|
|
|
|
|
|
return $conf instanceof UOJProblemConf ? $conf->conf : $conf;
|
2022-11-06 02:26:21 +00:00
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getProblemConf(string $where = 'data') {
|
|
|
|
|
if ($where === 'data') {
|
|
|
|
|
return UOJProblemConf::getFromFile($this->getDataFilePath('problem.conf'));
|
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getNonTraditionalJudgeType() {
|
|
|
|
|
$conf = $this->getProblemConf();
|
|
|
|
|
if (!($conf instanceof UOJProblemConf)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $conf->getNonTraditionalJudgeType();
|
|
|
|
|
}
|
2023-02-13 00:28:33 +00:00
|
|
|
|
|
|
|
|
|
public function syncData($user = null) {
|
|
|
|
|
return (new UOJProblemDataSynchronizer($this, $user))->sync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function addHackPoint($uploaded_input_file, $uploaded_output_file, $reason = null, $user = null) {
|
|
|
|
|
if ($reason === null) {
|
|
|
|
|
if (UOJHack::cur()) {
|
|
|
|
|
$reason = [
|
|
|
|
|
'rejudge' => '自动重测本题所有获得100分的提交记录',
|
|
|
|
|
'hack_url' => HTML::url(UOJHack::cur()->getUri())
|
|
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
$reason = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (new UOJProblemDataSynchronizer($this, $user))->addHackPoint($uploaded_input_file, $uploaded_output_file, $reason);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function uploadDataViaZipFile($new_data_zip) {
|
|
|
|
|
return (new UOJProblemDataSynchronizer($this))->upload($new_data_zip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function updateProblemConf($new_problem_conf) {
|
|
|
|
|
return (new UOJProblemDataSynchronizer($this))->updateProblemConf($new_problem_conf);
|
|
|
|
|
}
|
2022-11-06 02:26:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UOJProblem::$table_for_content = 'problems_contents';
|
|
|
|
|
UOJProblem::$key_for_content = 'id';
|
|
|
|
|
UOJProblem::$fields_for_content = ['*'];
|
|
|
|
|
UOJProblem::$table_for_tags = 'problems_tags';
|
|
|
|
|
UOJProblem::$key_for_tags = 'problem_id';
|