如何加强一道题目(详细揭秘)

加强一道题目是怎么回事呢?那么一道题目为什么会被加强,相信大家都很好奇。大家可能会感到很惊讶,一道题目为什么会被加强呢?但事实就是这样,小编也感到非常惊讶。


提交链接:https://www.luogu.com.cn/problem/U259119

欢迎大家来自测!

本来打算补一补这道咕了巨久的题目,然后发现你谷讨论区有人指出这题能加强。

然后花了大概半天的时间补掉这题。

然后开始咕咕咕地加强。

写了一个很复杂的生成树的 Gen,然后询问写挂以为树建挂了调了巨久……

好消息是,这个板子可以用于很多生成树的情景。

实现了:

  • \(f_p<p\) 的生成器。
  • \(n^{n-2}\) 颗树中随机选择的生成器。
  • 链、菊花、蒲公英的生成器。
  • 对边进行随机重标号、随机打乱。
  • 给边加随机权。
  • 校验树的合法性。

是以

using Vtype = std::vector<int>;
using Etype = std::vector<std::pair<int,int> >;
using EVtype = std::vector<std::pair<std::pair<int,int>,int> >;

的形式存储的,欢迎大家在下次造树时使用!

Code

树为 \(0\) 标号,要改为 \(1\) 标号请调用 Addlabel(E,1);

#include "testlib.h"

using Vtype = std::vector<int>;
using Etype = std::vector<std::pair<int,int> >;
using EVtype = std::vector<std::pair<std::pair<int,int>,int> >;

void Relabel(Etype&Edge,bool op1=true,bool op2=false,random_t&rng=rnd){
    int n=Edge.size()+1;
    Vtype To(n);
    for(int i=0;i<n;i++)
        To[i]=i;
    std::mt19937 r(rng.next(1u<<30));
    shuffle(To.begin(),To.end(),r);
    for(auto&e:Edge)
        e.first=To[e.first],e.second=To[e.second];
    if(op1)
        shuffle(Edge.begin(),Edge.end(),r);
    if(op2)for(auto&e:Edge)if(rng.next(2))
        std::swap(e.first,e.second);
}

void Addlabel(Etype&Edge,int v){
    for(auto&e:Edge)
        e.first+=v,e.second+=v;
}

Etype RandTree1(int n,int t=0,random_t&rng=rnd){
    Etype Edge;
    for(int i=1;i<n;i++)
        Edge.push_back({i,rng.wnext(i,t)});
    return Edge;
}

Etype RandTree2(int n,int t=0,random_t&rng=rnd){
    Etype Edge;
    Vtype A(n-2),D(n,1),u;
    for(int i=0;i<n-2;i++)D[A[i]=rng.wnext(n,t)]++;
    int id=0;
    for(int i=0;i<n;i++)if(D[i]==1){
        int p=i;
        do
        {
            if(id==n-2)break;
            D[p]=-1;
            int s=A[id++];
            Edge.push_back({s,p});
            if(--D[s]==1)p=s;else break;
        }
        while(p<i);
    }
    for(int i=0;i<n;i++)if(~D[i])u.push_back(i);
    Edge.push_back({u[0],u[1]});
    return Edge;
}

Etype Chain(int n,bool op=false){
    Etype Edge;
    for(int i=1;i<n;i++)
        if(op)
            Edge.push_back({i,i-1});
        else
            Edge.push_back({i-1,i});
    return Edge;
}

Etype Chrys(int n,int rot=0,bool op=false){
    if(rot>=n||rot<0)
        rot=0;
    Etype Edge;
    for(int i=0;i<n;i++)if(i!=rot){
        if(op)
            Edge.push_back({rot,i});
        else
            Edge.push_back({i,rot});
    }
    return Edge;
}

Etype Dande(int n,int mainp=-1,bool op=false){
    if(mainp>=n||mainp<0)
        mainp=n/2;
    Etype Edge;
    if(op){
        for(int i=0;i<mainp;i++)
            Edge.push_back({i,i+1});
        for(int i=mainp+1;i<n;i++)
            Edge.push_back({i,mainp});
    }else{
        for(int i=0;i<mainp;i++)
            Edge.push_back({i,mainp});
        for(int i=mainp+1;i<n;i++)
            Edge.push_back({i-1,i});
    }
    return Edge;
}

Vtype RandVal(int n,int up=2147483647,int down=0,random_t&rng=rnd){
    Vtype Ans(n);
    for(auto&e:Ans)
        e=rng.next(down,up);
    return Ans;
}

EVtype UpdateVal(Etype E,Vtype Val){
    if(E.size()!=Val.size())
        exit(0);
    EVtype Ans;
    int p=0;
    for(auto e:E)
        Ans.push_back({e,Val[p++]});
    return Ans;
}

Etype RemoveVal(EVtype E){
    Etype Ans;
    for(auto e:E)
        Ans.push_back(e.first);
    return Ans;
}

Vtype GetVal(EVtype E){
    Vtype Ans;
    for(auto e:E)
        Ans.push_back(e.second);
    return Ans;
}

std::vector<int>Way[60005];

bool Gone[60005];

void dfs(int p){
    if(Gone[p])return;
    Gone[p]=1;
    for(auto s:Way[p])
        dfs(s);
}

bool Valid(int n,Etype E){
    if(E.size()!=n-1u)
        return false;
    for(int i=0;i<n;i++)
        Way[i].clear(),Gone[i]=false;
    for(auto e:E){
        if(e.first<0||e.second<0||e.first>=n||e.second>=n)
            return false;
        Way[e.first].push_back(e.second);
        Way[e.second].push_back(e.first);
    }
    dfs(0);
    for(int i=0;i<n;i++)
        if(!Gone[i])
            return false;
    return true;
}

bool match(char*Sys,const char*C){
    while(*Sys&&*C)if(*(Sys++)!=*(C++))return false;
    return*Sys==*C;
}

int main(int argc,char**argv)
{
    registerGen(argc,argv,1);
    freopen(argv[1],"w",stdout);
    int n=atoi(argv[2]),q=atoi(argv[3]);
    n=rnd.wnext(1,n,1000),q=rnd.wnext(1,q,1000);
    Etype E;
    if(match(argv[4],"chain"))
        E=Chain(n);
    else if(match(argv[4],"chrys"))
        E=Chrys(n);
    else if(match(argv[4],"dande"))
        E=Dande(n);
    else if(match(argv[4],"r1"))
        E=RandTree1(n,argc<=5?0:atoi(argv[5]));
    else
        E=RandTree2(n,argc<=5?0:atoi(argv[5]));
    Relabel(E,1,1);
    if(!Valid(n,E)){
        fputs("qwq",stderr);
        puts("Invalid Data"),fflush(stdout);
        return 0;
    }
    Addlabel(E,1);
    EVtype E2=UpdateVal(E,RandVal(n-1,1000,1));
    printf("%d %d\n",n,q);
    for(auto e:E2)
        printf("%d %d %d\n",e.first.first,e.first.second,e.second);
    Vtype A(n,0);
    while(q--){
        int p=rnd.next(n);
        int v=rnd.next(std::max(-A[p],-1000),1000);
        A[p]+=v;
        printf("%d %d\n",p+1,v);
    }
    return 0;
}

然后就爽了。

使用我之前造数据的套路生成器,使用了 cpl1.cppcpl2.cpp

cpl1.cpp

// gen input

#include <stdio.h>
#include <stdlib.h>
bool match(char*Sys,const char*C){
    while(*Sys&&*C)if(*(Sys++)!=*(C++))return false;
    return*Sys==*C;
}
void runcode(char*Sys){
    fprintf(stderr,"%s\n",Sys),fflush(stderr);
    system(Sys);
}
char C[100005],D[300005],Sys[500005];
int main()
{
    system("g++ gen.cpp -o gen -Wall -std=c++11 -lm -Wl,--stack=33554432");
    FILE*fin=fopen("data_task.txt","r");
    int l,r;
    while(fscanf(fin,"%d%d",&l,&r)==2)
    {
        fgets(C,100000,fin);
        char*c=C;
        while(*c&&*c!='\r'&&*c!='\n')c++;
        *c='\0';
        sprintf(D,"gen %s",C);
        while(l<=r)
        {
            sprintf(Sys,D,l);
            runcode(Sys);
            l++;
        }
    }
    fclose(fin);
    return 0;
}

cpl2.cpp

// gen answer

#include <stdio.h>
#include <stdlib.h>
bool match(char*Sys,const char*C){
    while(*Sys&&*C)if(*(Sys++)!=*(C++))return false;
    return*Sys==*C;
}
void runcode(char*Sys){
    fprintf(stderr,"%s\n",Sys),fflush(stderr);
    system(Sys);
}
char C[100005],D[300005],Sys[500005];
int main()
{
    system("g++ std.cpp -o std -Wall -std=c++11 -lm -Wl,--stack=33554432");
    FILE*fin=fopen("data_name.txt","r");
    int l,r;
    while(fscanf(fin,"%d%d",&l,&r)==2)
    {
        fgets(C,100000,fin);
        char*c=C;
        while(*c&&*c!='\r'&&*c!='\n')c++;
        *c='\0';
        sprintf(D,"std < %s.in > %s.ans",C,C);
        while(l<=r)
        {
            sprintf(Sys,D,l,l);
            runcode(Sys);
            l++;
        }
    }
    fclose(fin);
    return 0;
}

然后 data_task.txtdata_name.txt 这么填。

data_task.txt

1 5 data\game_small%d.in 5000 2000 chain
6 10 data\game_small%d.in 5000 2000 chrys
11 15 data\game_small%d.in 5000 2000 dande
16 20 data\game_small%d.in 5000 2000 r1
21 25 data\game_small%d.in 5000 2000 r2
1 1 data\game%d.in 5000 2000 chain
2 2 data\game%d.in 5000 2000 chrys
3 3 data\game%d.in 5000 2000 dande
4 4 data\game%d.in 5000 2000 r1
5 5 data\game%d.in 5000 2000 r2
6 7 data\game%d.in 60000 60000 chain
8 9 data\game%d.in 60000 60000 chrys
10 11 data\game%d.in 60000 60000 dande
12 13 data\game%d.in 60000 60000 r1
14 15 data\game%d.in 60000 60000 r2
16 16 data\game%d.in 60000 60000 r1 10000
17 17 data\game%d.in 60000 60000 r1 -10000
18 18 data\game%d.in 60000 60000 r2 10000
19 20 data\game%d.in 60000 60000 r2 -10000
21 21 data\game%d.in 60000 60000 chain
22 22 data\game%d.in 60000 60000 chrys
23 23 data\game%d.in 60000 60000 dande
24 24 data\game%d.in 60000 60000 r1
25 25 data\game%d.in 60000 60000 r2

data_name.txt

1 25 data\game_small%d
1 25 data\game%d

完整造题文件下载链接

造完后本地用 Lemon 挑了四篇题解测了一下,结果是这样的:结果文件下载链接。(呜呜本地 LemonLime 打不开,只好用 Lemon 了)


那么这就是关于加强一道题目的事情了,大家有没有觉得很神奇呢?快来在评论区与小编互动吧!

posted @ 2022-11-03 16:09  myee  阅读(60)  评论(0编辑  收藏  举报