关于对拍
关于对拍
灵感来源: 洛谷P6327 和 <<算法竞赛进阶指南>>——李煜东大大
温馨提示:本篇文章的对拍是以 洛谷P6327 为基础的对拍,并不一定适用于所有题目(思路相同而代码不同)。
前言
在 年 月 日这平凡的一天,蒟蒻作者 在老师的逼迫下 鼓起勇气来挑战 区间加区间sin和 这道题,令人苦恼的是,作者花了一个上午都没有找到程序的错误,而洛谷的这道题也没有提供数据下载(疯狂暗示),于是作者的 魔鬼 老师让作者这个蒟蒻现场学对拍,因此就有了这篇文章。
正文
为了完成对拍,我们需要写 个程序,即以下 个程序。
* 一个随机数据生成器 (本文中命名为 )。
作用:快速生成随机数据大量随机数据,通过大数据找到程序的问题。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
//freopen("data.in","w",stdout);
srand(time(0));
int n=10;
cout<<n<<endl;
for (int i=1;i<=n;i++) cout<<rand()%10<<" ";
puts("");
int m=10;
cout<<m<<endl;
for (int i=1;i<=m;i++){
int op,l,r,v;
op=rand()%2+1;
l=rand()%n+1;
r=rand()%n+1;
if (l>r) swap(l,r);
if (op==1){
v=rand()%10;
cout<<op<<" "<<l<<" "<<r<<" "<<v<<endl;
}else {
cout<<op<<" "<<l<<" "<<r<<endl;
}
}
return 0;
}
* 一个由自己编写的 正解 程序,即我们提交上去的答案程序。(本文中将程序命名为 )
作用:负责出错。
错误代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
double s[N<<2],c[N<<2];
ll inc[N<<2];
const int BUFSIZE=50<<20;
char Buf[BUFSIZE+1],*buf=Buf;
template <class T>
inline void read(T&a){
for (a=0;*buf<'0' || *buf>'9';buf++) ;
while(*buf>='0' && *buf<='9'){
a=a*10+(*buf-'0');
buf++;
}
}
void push_up(int u){
s[u]=s[u<<1]+s[u<<1|1];
c[u]=c[u<<1]+c[u<<1|1];
}
void gx(int u,double s1,double c1,ll v){
double s2=sin(v),c2=cos(v);
s[u]=s1*c2+c1*s2;
c[u]=c1*c2-s1*s2;
inc[u]+=v;
}
void push_down(int u){
if (inc[u]){
gx(u<<1,s[u<<1],c[u<<1],inc[u]);
gx(u<<1|1,s[u<<1|1],c[u<<1|1],inc[u]);
inc[u]=0;
}
}
void build(int u,int l,int r){
if (l==r){
int v;
read(v);
s[u]=sin(v);
c[u]=cos(v);
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,int v){
if (x<=l && r<=y){
gx(u,s[u],c[u],v);
return;
}
int mid=l+r>>1;
push_down(u);
if (x<=mid) update(u<<1,l,mid,x,y,v);
if (y>mid) update(u<<1|1,mid+1,r,x,y,v);
push_up(u);
}
double query(int u,int l,int r,int x,int y){
if (x<=l && r<=y) return s[u];
int mid=l+r>>1;
double res=0;
if (x<=mid) res+=query(u<<1,l,mid,x,y);
if (y>mid) res+=query(u<<1|1,mid+1,r,x,y);
return res;
}
int main(){
fread(Buf,1,BUFSIZE,stdin);
int n;
read(n);
build(1,1,n);
int m;
read(m);
for (int i=1;i<=m;i++){
int op,l,r;
int v;
read(op),read(l),read(r);
if (op==1){
read(v);
update(1,1,n,l,r,v);
}else {
printf("%.1f\n",query(1,1,n,l,r));
}
}
return 0;
}
有无懂哥找一下这个程序错误qaq
正确代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
double s[N<<2],c[N<<2];
ll inc[N<<2];
const int BUFSIZE=50<<20;
char Buf[BUFSIZE+1],*buf=Buf;
template <class T>
inline void read(T&a){
for (a=0;*buf<'0' || *buf>'9';buf++) ;
while(*buf>='0' && *buf<='9'){
a=a*10+(*buf-'0');
buf++;
}
}
void push_up(int u){
s[u]=s[u<<1]+s[u<<1|1];
c[u]=c[u<<1]+c[u<<1|1];
}
void gx(int u,double s1,double c1,ll v){
double s2=sin(v),c2=cos(v);
s[u]=s1*c2+c1*s2;
c[u]=c1*c2-s1*s2;
inc[u]+=v;
}
void push_down(int u){
if (inc[u]){
gx(u<<1,s[u<<1],c[u<<1],inc[u]);
gx(u<<1|1,s[u<<1|1],c[u<<1|1],inc[u]);
inc[u]=0;
}
}
void build(int u,int l,int r){
if (l==r){
int v;
read(v);
s[u]=sin(v);
c[u]=cos(v);
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,int v){
if (x<=l && r<=y){
gx(u,s[u],c[u],v);
return;
}
int mid=l+r>>1;
push_down(u);
if (x<=mid) update(u<<1,l,mid,x,y,v);
if (y>mid) update(u<<1|1,mid+1,r,x,y,v);
push_up(u);
}
double query(int u,int l,int r,int x,int y){
if (x<=l && r<=y) return s[u];
int mid=l+r>>1;
double res=0;
push_down(u);
if (x<=mid) res+=query(u<<1,l,mid,x,y);
if (y>mid) res+=query(u<<1|1,mid+1,r,x,y);
return res;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
fread(Buf,1,BUFSIZE,stdin);
int n;
read(n);
build(1,1,n);
int m;
read(m);
for (int i=1;i<=m;i++){
int op,l,r;
int v;
read(op),read(l),read(r);
if (op==1){
read(v);
update(1,1,n,l,r,v);
}else {
printf("%.1f\n",query(1,1,n,l,r));
}
}
return 0;
}
* 一个由自己编写的 朴素解法(暴力解法) 。(本文中将程序命名为 )
作用:( 负责暴力 ) 提供正确答案,用于对拍。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
double s[N],c[N];
int a[N];
const int BUFSIZE=50<<20;
char Buf[BUFSIZE+1],*buf=Buf;
template <class T>
inline void read(T&a){
for (a=0;*buf<'0' || *buf>'9';buf++) ;
while(*buf>='0' && *buf<='9'){
a=a*10+(*buf-'0');
buf++;
}
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.ans","w",stdout);
fread(Buf,1,BUFSIZE,stdin);
//printf("%.1f",sin(1));
//return 0;
int n;
read(n);
for (int i=1;i<=n;i++) read(a[i]);
// for (int i=1;i<=n;i++) cout<<a[i]<<" ";
//puts("");
int m;
read(m);
for (int _=0;_<m;_++){
int op,l,r,v;
read(op),read(l),read(r);
if (op==1){
read(v);
for (int i=l;i<=r;i++) a[i]+=v;
}else {
double ans=0;
for (int i=l;i<=r;i++) ans+=sin(a[i]);
printf("%.1f\n",ans);
}
}
return 0;
}
* 一个对拍程序,前三个程序为此程序服务。(本文中命名为 )。
作用:完成最终的对拍。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
for (int T=1;T<=10000;T++){
system("D:\\lkr\\GEN.exe>data.in");
double st=clock();
system("D:\\lkr\\zhengjie.exe<data.in>data.out");
double ed=clock();
system("D:\\lkr\\baoli.exe<data.in>data.ans");
if (system("fc D:\\lkr\\data.out D:\\lkr\\data.ans")){
puts("Wrong Answer");
return 0;
}
else {
printf("Accepted, 测试点 #%d, 用时 %.0lfms\n",T,ed-st);
puts("");
}
}
return 0;
}
关于对拍的原理,在 <<算法竞赛进阶指南>> 中讲述的非常详细了,所以作者在这里只提几点。
:在编写随机数据生成器程序之前,一定要先输出一下 RAND_MAX ,一般的Windows系统的 这个值是 ,但也有不同的,所以要先输出这个值,这样才能确定你的 的范围。
:如何构造出一个在 范围中的数? 很简单,我们先拿出 ,然后让它对 取模,最后加上偏移量 就可以得到在给定范围内的数了。
: 我们都知道在 中的 也可以起到对拍作用,于是,在我们编写对拍程序时,可能会将其命名为 、 等(像作者一样),注意,绝对不能将程序命名为 ,否则在执行对拍程序的第十行代码时会出现问题(可以理解为程序的递推),若你已经将程序命名为 ,可以将程序的cpp文件名修改,并删除原有的 文件删除。 (PS:作者解决这个麻烦花了两小时)
: 在对拍前,一定要确保自己的朴素解法程序不出错,否则对拍得到的结果就是一场空。(PS:作者解决这个麻烦花了将近半小时)
: 最好将对拍的四个程序放在同一个文件夹中。
希望这篇文章可以帮助你更好地学习对拍。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!