交互题!!!

今天SDOI二轮培训(没错我又来找虐了)
D1T1是一道交互题。

然而我还是太弱。经验也不够。rand检验次数太少。推到推导也不会,遂保龄。

然而大体思路还是差不多的(剩下的题去一边)

题面

Problem A. 排列谜题(perm.c/cpp/pas)

Input file: N/A

Output file: N/A

Time limit: 1 seconds Memory limit: 256 megabytes

这是一道交互题,本题仅支持 C++。

小 y 有一个排列 p,小 z 也很想要知道小 y 这个排列到底是什么。
小 z 只知道这是一个 0 到 n − 1 的排列,下标从 0 开始。小 y 不想告诉小 z 这个排列,于是他规定了小 z 只能向他提出一个固定的问题,小 z 可以向他提出 3 个 0 到 n − 1 中的数字 a, b, c,而小 y 会告诉他 \(p^{-1}(x)=(p(a) ∗ p(b) + p(c))\)(所有运算都是在 \(mod~n\) 意义下的)。其中 \(p^{-1}(x)\) 表示满足 p(y) = x 的 y。由于小 y 没有什么耐心,所以小 z 希望能够尽快得到答案。

Notes

下发文件中有 perm.hpp、sample.cpp 两个文件,以下是详细说明,如果懒得看的话你也可以直接参照 sample.cpp 编写代码。

你需要在你代码的开头 #include ”perm.hpp” 来与交互库交互,你不应该实现 main 函数或试图进行任何文件输入输出,你只需要实现一个函数:std::vector guess(int n),它接受 n,返回一个长度为 n 的 std::vector 表示你猜出的排列。

你可以使用交互库提供的一个函数:int query_position(int a,int b,int c),这个函数接受 a, b, c,返回 \(p^{-1}(x)=(p(a) ∗ p(b) + p(c))\)

样例交互库(下发的 perm.hpp)会从标准输入读入排列。交互库不会占用超过 0.25s 时间和 64MB 内存。

交互库是固定的,即交互库会在调用你的 guess 函数之前生成好排列,不会动态调整。数据包含若干组数据,你的得分为最小值。

对于每个数据,假设你的程序没有得出正确的排列或者询问了非法的内容,则获得 0 分。

否则如果你调用了不超过 12512 次,可以获得 100 分,如果调用了超过 1000000 次,则获得 0 分。否则假设你调用了 \(time\) 次询问,你的得分将是\(\frac{1251200}{time}\)

//perm.cpp
#include"perm.hpp"
#include<bits/stdc++.h>


#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())
#define sqr(x) ((x)*(x))
#define GCD __gcd
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)

using namespace std;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;
typedef vector<int> VI;
typedef vector<VI> VII;

const int N=5005;



int n,p[N],nex[N],_0,_n1,_1,v[N][N];

std::vector<int> guess(int n){
    vector<int> Ans;
    if(n==1){
        Ans.push_back(0);
        return Ans;
    }
    
    memset(v,-1,sizeof(v));
    fo(i,0,n-1)p[i]=-1;
    fo(i,0,n-1){
        bool ok=true;
        fo(j,0,min(n-1,1000)){
            if(v[i][j]==-1)v[i][j]=query_position(i,j,j);
            if(v[i][j]!=j){ok=false;break;}
        }
        if(ok){
            _0=i;
            break;
        }
    }
    fo(i,0,n-1)if(i!=_0){
        bool ok=true;
        fo(j,0,min(n-1,1000)){
            if(v[i][j]==-1)v[i][j]=query_position(i,j,j);
            if(v[i][j]!=_0){ok=false;break;}
        }
        if(ok){
            _n1=i;
            break;
        }
    }
    p[_n1]=n-1;p[_0]=0;
    _1=query_position(_n1,_n1,_0);
    p[_1]=1;
    fo(i,0,n-1)nex[i]=query_position(_1,i,_1);
    for(int i=_1;i!=_n1;i=nex[i])p[nex[i]]=p[i]+1;
    for(int i=0;i<n;++i)Ans.push_back(p[i]);
    return Ans;
}
//sample
#include"perm.hpp"
#include<bits/stdc++.h>

using namespace std;

std::vector<int> guess(int n){
    vector<int> A;
    A.push_back(0);
    if(n==1)return A;
}

\(Solution\)

首先我们从一些具有特殊性质的数字找起:\(0~and~1\)

为什么? 首先\(0\)可以使得返回的数据中的\(p(a)p(b)\)变为\(0\),直接返回\(p(c)\)的位置

然后我们找\(1\),对于查询\(pos[1],x,pos[0]\),就直接返回\(x+1\)的位置,利用这个我们可以推出所有的答案。

然后这个查询次数好像是\(3n+\)一些错误的查询,据大佬分析,好像是在\(15000~6000\)之间波动(TQL

然后就是加速。

怎么加速呢?首先我们要记录,不能重复查重复的次数。然后我们要重复利用有限的信息。

如何利用,其实我们大部分的查询都花费在了搜寻\(1~and~0\)上了。而且这不能重复利用。

查询\(0\)时,我们调用的是\(query_position(i,j,j)==j\)i为当前的下标,j为枚举进行检验的下标

搜寻\(1\)时,我们调用的是\(query_positon(i,j,pos[0])\),i为当前的下标,j为所枚举进行检验i的下标,pos[0]为数值0所在的下标(后同

但我们似乎却忘了一个问题,这是在模意义下的。

为什么不搜索-1呢?啥?为什么

我们查询\(-1\)时,所调用的是\(query_position(i,j,j)==pos[0]\),可以看出,是和查询\(0\)时的参数是一样的。在加上记忆,差不多就能剩下许多的查询

然后因为有特殊情况,需要多次判断,这里没听懂,听说是\(2\sqrt{n}\)

然后因为我太菜了。没有标程,大佬的厚不下脸皮来贴,就不贴了233

猜你喜欢

转载自www.cnblogs.com/Lance1ot/p/9251145.html
今日推荐