关于lemon的spj和交互题的配置
SPJ
首先是 spj.cpp 的写法,下面是 \(lemon\) 的写法(一些线上 \(OJ\) 的写法都和 \(luogu\) 的类似,洛谷写的挺详细的,挂个链接):
模板
#include <bits/stdc++.h>
#include <climits>
#include <algorithm>
#include <fstream>//模板粘下来就行
using namespace std;
const int N = 5e5 + 10;
int n,nn;
int a[N],b[N] , an[N], bn[N],d[N];
bool vis[N];
vector<int>q[N];
set<int>p[N];
ifstream fin,fout,fstd;//模板粘下来就行
ofstream fscore,freport;//模板粘下来就行
void dfs(int u) {
vis[u]=0;
for(auto v:q[u]) {
if(p[u].find(v)!=p[u].end()||vis[v]) {
continue;
}
}
}
int main(int argc , char *argv[]) { //模板粘下来就行
fin.open(argv[1]);//打开data的.in文件
fout.open(argv[2]);//打开选手的输出文件
fstd.open(argv[3]);//打开data的.out文件,对于部分题目没有也可以
fscore.open(argv[5]);//判分用的
int sc = atoi(argv[4]);//这个会返回每个点多少分
int jd = 1;//得分的权重,最后返回sc*jd
fin >> n; //这个会读入.in的文件
for(int i=1;i<=n;i++) {
fin >> a[i] >> b[i];d[a[i]]++;d[b[i]]++;
q[a[i]].push_back(b[i]);
q[b[i]].push_back(a[i]);
}
int k;
fstd >> k;//这个会读入data的.out文件里的数据
fout >> nn;//这个会读入选手的输出文件里的数据
if(nn>n+1) jd=0;
if(k==-1) {
if(nn!=-1) jd=0;
}
else {
for(int i=1;i<=nn;i++) {
fout>>an[i]>>bn[i];d[an[i]]--;d[bn[i]]--;
p[an[i]].insert(bn[i]);
p[bn[i]].insert(an[i]);
}
dfs(1);
int f=0;
for(int i=1;i<=n;i++) {
if(vis[i]!=1) f=1;
if(d[i]==0) jd=0;
}
if(f==0) jd=0;
}
fscore << sc * jd << endl;//输出选手得分用的
fin.close();
fout.close();
fstd.close();
fscore.close();
return 0;
}
代码里有详细的注释,具体的写法因题而异。
配置
然后就是 \(lemon\) 的配置,这里以 \(lemonlime\) 为例,这个黄 \(lemon\) 也是同样的:

交互题:
这里以函数交互为例(IO交互一般可以转化成函数交互):
首先你需要写一个 grader.cpp
#include "a.h" //这是自己编写的 .h 文件,下面会有写法
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
int n,lim,ask_num;
string S;
int Ask(string s) {//选手可以调用的函数
int res=0; ask_num++;
if(ask_num>lim) {
cout<<"NO";exit(0);
}
for(int i=0;i<n;i++) {
if(s[i]==S[i]) {
res++;
}
}
return res;
}
signed main() {
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
cin>>n>>lim>>S;
string ans=solve(n,lim);//选手应该实现的函数
if(lim>ask_num) {//对于询问次数的限制
cout<<"NO";
return 0;
}
for(int i=0;i<n;i++) {
if(ans[i]!=S[i]) {
cout<<"NO";
return 0;
}
}
cout<<"YES";
return 0;
}
下面是 .h 的写法:
#include<bits/stdc++.h>
using namespace std;
int Ask(string s);
string solve(int n,int lim);
这样 .h 的文件就是一个桥梁的作用。
当然这样写在选手实现的函数里面也应该调用 .h 也就是在自己的文件开头加上 #include "a.h" 这样就可以正常调用函数了。
这是 \(lemonlime\) 的配置:

不同的写法:
一种是有
.h文件的(就是上面的示例)。另一种是不写
.h文件的,具体的就是需要在grader.cpp中调用选手实现的函数,也就是声明一下这个函数,这种情况选手实现的函数的全局变量不应该与grader.cpp里的变量重名,配置 \(lemon\) 时,交互库路径和交互库名称这两栏就空着,不写东西。
一种是在
grader.cpp里写freopen不选定向到标准输入和定向到标准输出(就是上面的示例)。另一种是不在
grader.cpp里写freopen选定向到标准输入和定向到标准输出。
重点:
交互题一般有一个判断是否正确的一个东西,我们这里称作密匙,少数情况下只判断答案正确与否的话,就把密匙放在 data 的 .out 文件里,这种情况选手下发文件的 grader.cpp 的密匙与 data 的密匙不一样,这样就是防止选手攻击交互库,但是交互题大多数的判分标准是询问次数,所以一般与 spj 配合食用。
它们两者之间并不冲突,所以可以同时书写放在 data 里面。
关于lenmon的注意事项:
配置交互题的时候,返回
超出时间限制,有时候是不同写法2中的一些东西冲突了,或是读入不全,就是读入的比data里的多。返回编译错误,
ld returned 1就是已经运行了,可能是grader.cpp运行了没关,重新测评或者请一下后台。返回
程序不能运行检查编译器版本是否符合计算机版本,一般是64位和32位冲突了。

spj 和 交互题
浙公网安备 33010602011771号