对拍方法

对拍很简单,原理是:比较 \(2\) 个代码的输出,也就是用暴力代码检查你的代码的正确性。而只需要你记住程序怎么打就行。

对拍的程序有 \(4\) 个:

  • my.cpp:你的程序。通常是不确定是否切题的时候,再决定打对拍(做接下来的事)。
  • bl.cpp:暴力程序。一般情况下,造数据时要考虑到其时间复杂度。而且,如果暴力的时间复杂度过高(如指数级别),有可能整场比赛结束了都跑不完,这个时候就不要打对拍了。
  • data.exe:用来造数据的,具体情况视题目而定。最好是记常用的方法:比如造树、图等。
  • checker.exe:用来自动运行前 \(3\) 个程序,并比较你的程序和暴力程序的输出。

顺便提一嘴,你的程序都要加文操,方便 \(\text{checker}\) 自动运行程序。

P3178 [HAOI2015] 树上操作 为例。

my.cpp

// 对拍方法(正解)
#include<bits/stdc++.h>

typedef int IT;
typedef long long LL;
typedef __int128 int128;
typedef double DB;
typedef long double LDB;

#define pb push_back
#define fst first
#define sec second
#define psh push
#define mkp make_pair
#define PII pair<IT,IT>
#define PLI pair<LL,IT>
#define lowbit(x) ((x)&(-x))
using namespace std;

const int N=1e5+10,ES=N<<1;

int n,m;
int a[N];

int ecnt,head[N],nxt[ES],to[ES];
void add(int x,int y){
    nxt[++ecnt]=head[x];
    head[x]=ecnt;
    to[ecnt]=y;
    return;
}

int dep[N],siz[N],fa[N],son[N];
void dfs1(int x,int fth){
    dep[x]=dep[fth]+1;
    siz[x]=1;
    fa[x]=fth;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fth) continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]])
            son[x]=y;
    }
}
int top[N];
int id[N],idx;
int newa[N];
void dfs2(int x,int ntop){
    top[x]=ntop;
    id[x]=++idx;
    newa[idx]=a[x];
    if(!son[x]) return;
    dfs2(son[x],ntop);
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y!=fa[x]&&y!=son[x])
            dfs2(y,y);
    }
}

struct Seg_Tree{
    LL sum;
    LL tag;
}t[N<<2];
void pushup(int rt){
    t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
    return;
}
void downtag(int rt,int l,int r,LL v){
    t[rt].sum+=(r-l+1)*v;
    t[rt].tag+=v;
    return;
}
void pushdown(int rt,int l,int r){
    if(t[rt].tag){
        int mid=(l+r)>>1;
        downtag(rt<<1,l,mid,t[rt].tag);
        downtag(rt<<1|1,mid+1,r,t[rt].tag);
        t[rt].tag=0;
    }
}
void build(int rt,int l,int r){
    if(l==r){
        t[rt].sum=newa[l];
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
    return;
}
void modify(int rt,int l,int r,int ml,int mr,LL v){
    if(r<ml||mr<l) return;
    if(ml<=l&&r<=mr){
        downtag(rt,l,r,v);
        return;
    }
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    modify(rt<<1,l,mid,ml,mr,v);
    modify(rt<<1|1,mid+1,r,ml,mr,v);
    pushup(rt);
    return;
}
LL query(int rt,int l,int r,int ql,int qr){
    if(r<ql||qr<l) return 0;
    if(ql<=l&&r<=qr) return t[rt].sum;
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    return query(rt<<1,l,mid,ql,qr)+
        query(rt<<1|1,mid+1,r,ql,qr);
}

LL SP_query(int x,int y){
    LL ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ans+=query(1,1,n,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans+=query(1,1,n,id[x],id[y]);
    return ans;
}

int main(){
    freopen("data.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }

    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);

    for(int i=1;i<=m;i++){
        int opt,x,a;
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&x,&a);
            modify(1,1,n,id[x],id[x],a);
        }
        else if(opt==2){
            scanf("%d%d",&x,&a);
            modify(1,1,n,id[x],id[x]+siz[x]-1,a);
        }
        else if(opt==3){
            scanf("%d",&x);
            printf("%lld\n",SP_query(1,x));
        }
    }
    return 0;
}

bl.cpp

// 对拍方法(暴力)
#include<bits/stdc++.h>

typedef int IT;
typedef long long LL;
typedef __int128 int128;
typedef double DB;
typedef long double LDB;

#define pb push_back
#define fst first
#define sec second
#define psh push
#define mkp make_pair
#define PII pair<IT,IT>
#define PLI pair<LL,IT>
#define lowbit(x) ((x)&(-x))
using namespace std;

const int N=1e5+10,ES=N<<1;

int n,m;
int a[N];

int ecnt,head[N],nxt[ES],to[ES];
void add(int x,int y){
    nxt[++ecnt]=head[x];
    head[x]=ecnt;
    to[ecnt]=y;
    return;
}

int dep[N],siz[N],fa[N];
int id[N],idx;
void dfs(int x,int fth){
    dep[x]=dep[fth]+1;
    siz[x]=1;
    fa[x]=fth;
    id[x]=++idx;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fth) continue;
        dfs(y,x);
        siz[x]+=siz[y];
    }
}

LL d[N];

int main(){
    freopen("data.in","r",stdin);
    freopen("bl.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }

    dfs(1,0);
    for(int i=1;i<=n;i++) d[id[i]]=a[i];
    for(int i=1;i<=m;i++){
        int opt,x,y;
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&x,&y);
            d[id[x]]+=y;
        }
        else if(opt==2){
            scanf("%d%d",&x,&y);
            for(int i=id[x];i<=id[x]+siz[x]-1;i++)
                d[i]+=y;
        }
        else if(opt==3){
            scanf("%d",&x);
            int t=x;
            LL ans=0;
            while(t!=1){
                ans+=d[id[t]];
                t=fa[t];
            }
            ans+=d[1];
            printf("%lld\n",ans);
        }
    }
    return 0;
}

data.cpp

// 对拍方法(造数据)
#include<bits/stdc++.h>

typedef int IT;
typedef long long LL;
typedef __int128 int128;
typedef double DB;
typedef long double LDB;

#define pb push_back
#define fst first
#define sec second
#define psh push
#define mkp make_pair
#define PII pair<IT,IT>
#define PLI pair<LL,IT>
#define lowbit(x) ((x)&(-x))
using namespace std;

const int N=2e5+10;

int n,m;

int fa[N];
int findfa(int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}

struct edge{
    int u,v;
}e[N];
int ord[N];

int main(){
    srand((int)(time(0)));
    freopen("data.in","w",stdout);
    n=10;m=10000;
    printf("%d %d\n",n,m);
    for(int i=1;i<=n;i++){
        int x=rand()%IT(1e6)+1;
        printf("%d ",x);
    }putchar('\n');
    // for(int i=2;i<=n;i++){
    //     e[i].u=i;
    //     e[i].v=rand()%(i-1)+1;
    // }
    // for(int i=1;i<n;i++) ord[i]=i+1;
    // random_shuffle(ord+1,ord+n);
    // for(int i=n;i>=2;i--) ord[i]=ord[i-1];
    // for(int i=2;i<=n;i++){
    //     printf("%d %d\n",e[ord[i]].u,e[ord[i]].v);
    // }
    for(int i=1;i<=n;i++) fa[i]=i;
    int i=1;
    while(i<=n-1){
        int x=rand()%n+1,y=rand()%n+1;
        while(x==y) y=rand()%n+1;
        int p=findfa(x),q=findfa(y);
        if(p!=q){
            fa[p]=q;
            i++;
            printf("%d %d\n",x,y);
        }
    }

    int R=1e6+1e6+1;
    for(int i=1;i<=m;i++){
        int opt,x,a;
        opt=rand()%3+1;
        printf("%d ",opt);
        x=rand()%n+1;
        a=rand()%R;
        a-=1e6;
        if(opt==1|opt==2){
            printf("%d %d\n",x,a);
        }
        else if(opt==3) printf("%d\n",x);
    }
    return 0;
}

checker.cpp

// 对拍方法(checker)
#include<bits/stdc++.h>

typedef int IT;
typedef long long LL;
typedef __int128 int128;
typedef double DB;
typedef long double LDB;

#define pb push_back
#define fst first
#define sec second
#define psh push
#define mkp make_pair
#define PII pair<IT,IT>
#define PLI pair<LL,IT>
#define lowbit(x) ((x)&(-x))
using namespace std;

const int N=2e5+10;

DB st,ed,mt;

int main(){
    for(int t=1;t<=500;t++){
        printf("Task %d:\n",t);

        printf("Generating the data.\n");
        system("data.exe");// 造数据

        printf("Running bl.cpp program...\n");
        st=clock();
        system("bl.exe");// 运行暴力程序
        ed=clock();
        mt=(ed-st)/1e3;
        printf("It used %.3lf s.\n",mt);

        printf("Running my.cpp program...\n");
        st=clock();// 计时
        system("my.exe");// 运行你的程序
        ed=clock();// 计时
        mt=(ed-st)/1e3;// 计算
        printf("You used %.3lf s.\n",mt);// 运行所耗时间
        if(mt>1){// 超时
            printf("TLE!\n");
            return 0;
        }
        if(!system("fc my.out bl.out")) printf("AC!\n\n");// 输出一样
        else{// 不一样
            printf("WA!\n");
            return 0;
        }
    }
    printf("Congratulations!\n");// 这些数据都对了
    return 0;
}

(提醒一下同机房的同学:你可以对着打,但不要直接复制)

对拍是个好东西,如果记住了,有时能在考场上发挥大作用。

posted @ 2025-02-10 18:42  YuYuanPQ  阅读(148)  评论(1)    收藏  举报