对拍Bete
前言
那天LZY需要对拍,题目样例给的是一个图,LZY发现自己的对拍程序并不能生成图。
这时候msjing站出来说:
我有随机生成图的程序。
这么好?
于是我直接拿来用。
用着用着不对劲。
- 你这图生成的什么玩意?
- msjing不知道
靠别人不如靠自己,在我坚持了3.1415926个小时后,终于写成了一个较为强大的随机样例生成器,大部分常用生成器均已打包进了函数里。
完整对拍程序
下载
注意:这个有亿点锅。
| 版本(胡扯) | 系统 | 语言 | 下载 |
|---|---|---|---|
| 1.2.0(测试) | Windows | C++14及以上 | 链接 |
| 1.1.5(测试) | Windows | C++14及以上 | 链接 |
| 1.1.4(正式) | Windows | C++14及以上 | 链接 |
| 1.1.3(正式) | Windows | C++14及以上 | 链接 |
| 1.1.2(测试) | Windows | C++14 | 链接 |
| 1.1.1(正式) | Windows | C++14 | 链接 |
使用教程[以1.1.4(正式)为准]
- 将文件解压到桌面(只有解压到桌面快捷方式才起作用!)。
- 打开解压后的「对拍」文件夹。
- 分别打开「待测程序」、「正确程序」将待测代码与暴力/正确代码放进去,要保留
freopen文件头(要是没了去文章后面Copy)。 - 打开「随机样例生成器」按下面的这个教程造样例。
- 准备完成后运行「开始」,按下回车即可开始对拍。
若有 Special Judge 的需要,请打开「Face-off」文件夹中的「Checker.cpp」文件,返回值与报错信息的对应关系均已注释,其中「corr」为从正确程序的输出中输入,「test」为从待测程序的输出中输入。
随机样例生成器
随机样例生成器
#include<bits/stdc++.h>
#include<tuple>
#include<vector>
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define dbl double
using namespace std;
mt19937_64 num(time(0));
template<typename T>inline T INT(T l,T r,char out=0){T x=num()%(r-l+1)+l;if(out)cout<<x<<out;return x;}
template<typename T>inline vector<T> INT(int len,T l,T r,char out=0,char out2=0){vector<T> v(len);for(int i=0;i<len;++i) v[i]=INT(l,r,out);if(out2) cout<<out2;return v;}
template<typename T>inline T FLO(T l,T r,char out=0,int pos=3){
uniform_real_distribution<long double> flo(l,r);
T x=flo(num);
if(out) cout<<fixed<<setprecision(pos)<<x<<out;
return x;
}
template<typename T>inline vector<T> FLO(int len,T l,T r,char out=0,char out2=0,int pos=3){
vector<T> v(len);
for(int i=0;i<len;++i) v[i]=FLO(l,r,out,pos);
if(out2) cout<<out2;
return v;
}
inline char CH(char l,char r,char out=0){char x=num()%(r-l+1)+l;if(out)cout<<x<<out;return x;}
inline char CH(string s,char out=0){char x=s[INT(0,(int)s.size()-1)];if(out)cout<<x<<out;return x;}
inline string STR(int len,char l,char r,char out=0,char out2=0){string s;for(int i=1;i<=len;++i) s+=CH(l,r,out);if(out2) cout<<out2;return s;}
template<typename T> inline T Pick(initializer_list<T> s,char out=0){
T x=*(s.begin()+num()%s.size());
if(out) cout<<x<<out;
return x;
}
template<typename T>inline vector<T> Pick(int len,initializer_list<T> s,char out=0,char out2=0){
vector<T> v(len);
for(int i=0;i<len;++i) v[i]=Pick(s,out);
if(out2) cout<<out2;
return v;
}
pair<int,int> ITV(int l,int r,bool out=0){
int x=INT(l,r),y=INT(l,r);
if(x>y) swap(x,y);
if(out) cout<<x<<' '<<y;
return {x,y};
}
auto Graph(int point,int edge,int weightl=0,int weightr=0,vector<string> t={}){
map<string,bool> type;
for(string s:t) type[s]=1;
bool repeat=type["repeat"],directed=type["directed"],
forest=type["forest"],self=type["self"],
heap=type["max-heap"]|type["min-heap"]|type["heap"];
vector<tuple<int,int,int> > g;
map<pair<int,int>,bool> vis;
vector<int> tmp;
if(type["max-heap"]) for(int i=point;i>=1;--i) tmp.emplace_back(i);
else for(int i=1;i<=point;++i) tmp.emplace_back(i);
if(!heap) shuffle(tmp.begin(),tmp.end(),num);
if(!forest)
for(int i=1,x,y;i<(int)tmp.size();++i){
x=tmp[INT(0,i-1)],y=tmp[i];
if(!repeat){
if(directed)vis[{x,y}]=1;
else vis[{x,y}]=vis[{y,x}]=1;
}
g.emplace_back((tuple<int,int,int>){x,y,INT(weightl,weightr)});
}
if(!forest) edge-=point-1;
if(!type["tree"])
while(edge>0){
int x=INT(1,point),y=INT(1,point);
while(vis[{x,y}]||(x==y&&!self)) x=INT(1,point),y=INT(1,point);
if(!repeat){
if(directed)vis[{x,y}]=1;
else vis[{x,y}]=vis[{y,x}]=1;
}
g.emplace_back((tuple<int,int,int>){x,y,INT(weightl,weightr)});
edge--;
}
if(type["value"]) for(auto x:g)cout<<get<0>(x)<<' '<<get<1>(x)<<' '<<get<2>(x)<<'\n';
else if(type["out"]) for(auto x:g)cout<<get<0>(x)<<' '<<get<1>(x)<<'\n';
return g;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("example.in","w",stdout);
return 0;
}
使用教程[以1.1.4(正式)为准]
随机生成整数INT()
使用INT()函数可以随机生成整数,返回值类型与传入参数 \(l,r\) 有关。
函数参数:
随机生成整数,返回一个整数。
- \(l,r\) (必要参数)表示生成随机数的范围 \([l,r]\)。
- \(out\) (非必要参数)表示是否输出及输出类型,默认为 \(0\),即不输出,若传入「空格」则表示将生成的随机数输出并输出后输出「空格」。
生成随机整数数列,返回一个vector。
- \(len\) 表示生成随机数列的长度。
- \(l,r\) 同上。
- \(out\) 表示数列中每一个数的输出类型,默认为 \(0\)。
- \(out2\) 表示整个序列的输出类型,默认为 \(0\)。
注意:
为避免传参混乱的问题,请不要写类似 \(INT(int,int,int)\) 的传参。
随机生成浮点数FLO()
使用FLO()函数可以生成随机浮点数,返回值类型与 \(l,r\) 有关。
函数参数:
随机生成浮点数,返回一个浮点数。
- \(l,r,out\) 同上。
- \(pos\) 表示输出时小数的位数。
生成随机浮点数数列,返回一个vector。
- \(len,l,r,out,out2,pos\) 同上。
随机生成字符(串)CH()/STR()
使用CH()函数可以生成一个字符,STR()可以生成一个字符串。
函数传参:
生成一个字符,返回char。
- \(l,r,out\) 同上。
生成一个字符串,返回一个string。
- \(len,l,r,out,out2\) 同上。
随机选择Pick()
使用Pick()可以在一个有限集合内选择元素。
函数传参:
在集合中选择一个元素,返回该元素。
- \(s\) 传入的集合,集合内元素使用花括号{}包裹,逗号分割。
- \(out\) 同上。
多次随机选择元素组成一个元素列,返回一个vector。
- \(len,s,out,out2\) 同上。
随机生成区间ITV()
使用ITV(),随机生成一个区间。
函数传参:
生成 \([l,r]\) 的一个子区间,返回一个pair<int,int>。
- \(l,r\) 区间范围。
- \(out\) 同上。
随机生成图Graph()
使用Graph函数可以快速生成一个图。
完整参数如下。
Graph(int point,int edge,int weightl=0,int weightr=0,vector<string> t={})。
point表示节点数。edge表示边数。weightl与weightr表示权值的范围。
最后的vector<string> t={}表示指令。
tree指令可以让函数强制生成一颗树,只在乎节点数,不在乎边数。forest指令允许函数生成的图不连通,即森林。directed指令会使函数认为\(x \rightarrow y\)与\(y \rightarrow x\)不是一条边,即生成有向图。out指令可以让函数直接将这颗树输出。value指令可以让函数输出时额外输出边权。repate指令可以允许函数生成重边。self指令可以允许函数生成自闭环。heap类命令可以让函数生成的树满足堆性质,默认为小根堆,min-heap、max-heap分别对应小根堆、大根堆。
auto g=Graph(10,10); //生成10个节点10条边的基环树。
auto g=Graph(10,100,1,1000,{"value","repeat","forest"});
//生成一个10个节点100条边、可重边、权值在 1~1000 之间的带边权的无向图,并带权输出,同时将图返回给变量 g。其中允许生成的图不连通。
注意
-
若你不传任何指令,函数默认生成一个不重边、无自闭环的无向联通图,返回值类型为
vector<tuple<int,int,int> >,tuple中的三个元表示 边与权值。 -
此生成随机图函数为不完整版,生成速度较慢,请不要使用它生成规模 \(1e5\) 以上的树,若要生成 \(1e6\) 规模的图请加上
forest和repeat指令。 -
若没有
forest指令,函数会先生成一颗树,再在树的基础上加边,也就是说如果传入的边数小于节点数减一,那么函数将返回一颗树。 -
若没有
repeat指令,传入的边数远远大于节点数的话,函数会四掉的。 -
仍要注意的是无论你是否选择输出,函数都会将图返回。
答案比较器
答案比较器
#include<bits/stdc++.h>
using namespace std;
ifstream corr,test;
/*
return 0: Accepted
return 1: Wrong answer: Objective wrong answer
return 2: Wrong answer: Output longer than answer
return 3: Wrong answer: Output shorter than answer
return -1: System error: can't find corrent.out
return -2: System error: can't find tested.out
else System error: Unknown Error
*/
int Judge(){
//Write Special Judge here
string s1,s2;
bool f=1;
while(corr>>s1&&test>>s2){
if(s1!=s2) f=0;s1="",s2="";
}
corr>>s1;test>>s2;
if(s1==""&&s2!="") return 2;
if(s2==""&&s1!="") return 3;
if(f) return 0;
else return 1;
}
signed main(){
ios::sync_with_stdio(0);
corr.tie(0),test.tie(0);
corr.open("corrent.out");
test.open("tested.out");
if(!corr.is_open()) return -1;
if(!test.is_open()) return -2;
return Judge();
}
对拍启动器
对拍启动器
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
void color(int c) {
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, c);
}
bool flag_check=1;
void sleep(){
char c=cin.get();
string s;
while(c!='\n') s+=c,c=cin.get();
if(s=="/close check") flag_check=0;
}
bool check(){
ifstream G,C,T;
G.open("Generator.exe",ios::binary);
C.open("Correct.exe",ios::binary);
T.open("tested.exe",ios::binary);
bool f=0;
if(!G.is_open()||!C.is_open()||!T.is_open()) f=1;
if(f){
color(12);
if(!G.is_open()) cerr<<"Warning: Generator hasn't been compiled.\n";
if(!C.is_open()) cerr<<"Warning: Correct hasn't been compiled.\n";
if(!T.is_open()) cerr<<"Warning: Tested hasn't been compiled.\n";
color(7);
sleep();
return 1;
}
G.close(),C.close(),T.close();
return 0;
}
int main(){
int tot=0;
clock_t st,ed;
sleep();
while(1){
if(flag_check) if(check()) continue;
cerr<<"test "<<++tot<<": \t";
system("Generator.exe");
system("Correct.exe");
st=clock();
int tes=system("Tested.exe");
ed=clock();
double tim=(double)ed-st;
if(tes){
color(6);
cerr<<"Runtime Error: Your program returned "<<tes<<".\t\t";
cerr<<tim<<" ms\n";
color(7);
sleep();
continue;
}
int res=system("Checker.exe");
switch(res){
case -2:
color(13),cerr<<"System Error: Can't find tested.out.\t\t";break;
case -1:
color(13),cerr<<"System Error: Can't find corrent.out.\t\t";break;
case 0:
color(10),cerr<<"Accepted.\t\t";break;
case 1:
color(4),cerr<<"Wrong Answer: Objective wrong answer.\t\t";break;
case 2:
color(4),cerr<<"Wrong Answer: Output longer than answer.\t\t";break;
case 3:
color(4),cerr<<"Wrong Answer: Output shorter than answer.\t\t";break;
default:
color(13),cerr<<"System Error: Unknow Error.\t\t";break;
}
cerr<<tim<<" ms\n";
color(7);
if(res) sleep();
}
return 0;
}
正确程序头
freopen("example.in","r",stdin);
freopen("corrent.out","w",stdout);
待测程序头
freopen("example.in","r",stdin);
freopen("tested.out","w",stdout);
更新日志
- 2026/7/25 16:05 略微降低了生成树的空间开销。
- 2026/7/25 16:39 Graph函数更新了
heap、min-heap、max-heap命令。更新了排版。 - 2026/7/25 20:51 加入了答案比较器、对拍启动器、正确程序头与待测程序头,至此,完整对拍程序已经完工。
- 2026/7/26 10:39 更新1.1测试版,加入计时功能。
- 2026/7/26 10:48 将此文章同步与洛谷。
- 2026/7/26 19:14 更新1.1.1正式版优化了Special Judge的书写难度,新增了
RE、UKE报错。 - 2026/7/26 20:19 修复了一些BUG。由于不同编译器对中文注释的处理不同,将中文注释改为了英文注释,并优化了文件的大小。
- 2026/7/27~2026/7/31 陆续发布了1.1.2(测试)、1.1.3(正式)、1.1.4(正式)
更新内容:
1.1.4(正式):
- 加入了Warning报错。
- 正式加入报错信息说明。
- 优化了Special Judge的书写环境。
- 优化了文章排版。
1.1.3(正式):
- 修复了1.1.2模板函数的锅。
- 更改了「随机样例生成器」的生成随机数函数。
- 加入了随机浮点数的生成。
- 从原函数分离出Pick()函数。
1.1.2(测试):
- 尝试加入报错信息说明。
- 使用模板函数合并相似函数(也出了巨大的锅)。
1.1.1(正式):
- 优化了Special Judge书写。
- 新增
RE、UKE报错。
1.1(测试):
- 加入计时功能。
1.0 :
- 完整对拍程序,可以完成对拍。

浙公网安备 33010602011771号