动态线条
动态线条end

关于对拍

关于对拍

灵感来源: 洛谷P6327<<算法竞赛进阶指南>>——李煜东大大

温馨提示:本篇文章的对拍是以 洛谷P6327 为基础的对拍,并不一定适用于所有题目(思路相同而代码不同)。

前言

2020815 日这平凡的一天,蒟蒻作者 在老师的逼迫下 鼓起勇气来挑战 区间加区间sin和 这道题,令人苦恼的是,作者花了一个上午都没有找到程序的错误,而洛谷的这道题也没有提供数据下载(疯狂暗示),于是作者的 魔鬼 老师让作者这个蒟蒻现场学对拍,因此就有了这篇文章。


正文

为了完成对拍,我们需要写 4 个程序,即以下 4 个程序。

*1 一个随机数据生成器 (本文中命名为 gen )。

​ 作用:快速生成随机数据大量随机数据,通过大数据找到程序的问题。

代码如下:

#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;
}

*2 一个由自己编写的 正解 程序,即我们提交上去的答案程序。(本文中将程序命名为 zhengjie

作用:负责出错。

错误代码如下:

#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;
}

*3 一个由自己编写的 朴素解法(暴力解法) 。(本文中将程序命名为 baoli

作用:( 负责暴力 ) 提供正确答案,用于对拍。

代码如下:

#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;
}

*4 一个对拍程序,前三个程序为此程序服务。(本文中命名为 duipai )。

作用:完成最终的对拍。

代码如下:

#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;
}

关于对拍的原理,在 <<算法竞赛进阶指南>> 中讲述的非常详细了,所以作者在这里只提几点。

1 :在编写随机数据生成器程序之前,一定要先输出一下 RAND_MAX ,一般的Windows系统的 这个值是 32767 ,但也有不同的,所以要先输出这个值,这样才能确定你的 rand() 的范围。

2 :如何构造出一个在 [x,y] 范围中的数? 很简单,我们先拿出 rand() ,然后让它对 y 取模,最后加上偏移量 x 就可以得到在给定范围内的数了。

3 : 我们都知道在 cmd 中的 fc 也可以起到对拍作用,于是,在我们编写对拍程序时,可能会将其命名为 fcFC 等(像作者一样),注意,绝对不能将程序命名为 fc ,否则在执行对拍程序的第十行代码时会出现问题(可以理解为程序的递推),若你已经将程序命名为 fc ,可以将程序的cpp文件名修改,并删除原有的 fc.exe 文件删除。 (PS:作者解决这个麻烦花了两小时)

4: 在对拍前,一定要确保自己的朴素解法程序不出错,否则对拍得到的结果就是一场空。(PS:作者解决这个麻烦花了将近半小时)

5: 最好将对拍的四个程序放在同一个文件夹中。

希望这篇文章可以帮助你更好地学习对拍。

posted @   冘木  阅读(185)  评论(1编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· 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 让容器管理更轻松!
Live2D
欢迎阅读『关于对拍』
动态线条
动态线条end
点击右上角即可分享
微信分享提示