【UOJ83】【UR #7】水题出题人(提交答案题)

点此看题面

大致题意: 给你若干份排序的代码,共\(6\)个子任务,每个子任务让你构造数据使得一份代码用时在给定的\(T\)以内,另一份代码用时超过\(2000000\)。

子任务\(1\):归并排序\(AC\),计数排序\(TLE\)

很简单,要想让计数排序\(TLE\),自然是要让值域尽量大。

由于\(T=7\),因此\(n\)恰好为\(1\),则我们随便选取一个较大的数作为被排序的数即可。

子任务\(2\):冒泡排序\(AC\),选择排序\(TLE\)

这个子任务,我们可以选取一大堆相同的数,然后在里面混进一个不同的数,这样就能过了。

至于数的总数混进数的位置,大概只能靠人肉二分了。

造数据代码如下:

#include<bits/stdc++.h>
#define N 1990//数的总数
#define M 1469//混进数的位置
using namespace std;
int main()
{
    freopen("Shion2.out","w",stdout);
    register int i;cout<<N<<endl;
    for(i=1;i^M;++i) cout<<19<<endl;//输出19(19是我的幸运数字)
    cout<<24<<endl;//混进一个24
    for(i=1;i<=N-M;++i) cout<<19<<endl;//接着输出19
    return 0;
}

子任务\(3\):归并排序\(AC\),快速排序\(TLE\)

经过测试,容易发现归并排序时间复杂度是稳定的。

则通过人肉二分,我们可以求出最大的\(n\)为\(1984\)。

然后就要考虑在这个限制之下如何卡快排。

凭常识可知,快排的时间复杂度是很不稳定的,最坏情况下可以卡到\(O(n^2)\),而这样就足够了。

考虑到在最坏情况下,我们应使中间的数最小(仔细去认真调试一遍快排的代码即可发现),而且每次操作后其实就相当于把最左边的数和中间的数换个位置

所以,我就写了个递归函数模拟快排来求答案。

造数据代码如下:

#include<bits/stdc++.h>
#define N 1984
using namespace std;
int p[N+5],ans[N+5];
void DivideAndSolve(int l,int r,int v)//模拟快排来求答案
{
    int x=l+r>>1,i=l,j=x;!ans[p[x]]&&(ans[p[x]]=++v);//使中间的数最小
    p[i]^=p[j]^=p[i]^=p[j],++i<r&&(DivideAndSolve(i,r,v),0);//交换最左边的数和中间的数
}
int main()
{
    freopen("Shion3.out","w",stdout);
    register int i;cout<<N<<endl;for(i=1;i<=N;++i) p[i]=i;//初始化每个位置上是原序列中的第i个数(因为之后会换位置)
    for(DivideAndSolve(1,N,0),i=1;i<=N;++i) cout<<ans[i]<<endl;//递归,然后输出答案
    return 0;
}

子任务\(4\):计数排序\(AC\),冒泡排序\(TLE\)

卡冒泡排序,需要构造逆序对。我们可以构造出一个序列,使其数字逐个递减。

由于要让计数排序\(AC\)且\(T\)较小,所以值域不能太大。

因此我们可以使每个数重复多次,然后人肉二分数的总数序列中数的最大值(这样可以同时求出每个数字的重复次数),就可以了。

造数据代码如下:

#include<bits/stdc++.h>
#define N 1012//数的总数
#define M 30//序列中数的最大值
using namespace std;
int main()
{
    freopen("Shion4.out","w",stdout);
    register int i,j;cout<<N<<endl;
    for(i=M;i;--i) for(j=1;j<=N/M;++j) cout<<i<<endl;//递减输出每个数,且每个数重复多次
    for(j=1;j<=N-(N/M)*M;++j) cout<<0<<endl;//由于不能恰好除尽,因此剩余的个数我们输出0
    return 0;
}

子任务\(5\):选择排序\(AC\),冒泡排序\(TLE\)

又是要卡冒泡排序,但这次没有了值域的限制,直接二分递归构造序列即可。

造数据代码如下:

#include<bits/stdc++.h>
#define N 1004
using namespace std;
int ans[N+5];
void DivideAndSolve(int l,int r,int v)//二分构造序列
{
    if(l>r) return;//当左边界大于右边界时,退出
    int mid=l+r>>1;ans[mid]=v,//给中间的数赋值
    DivideAndSolve(l,mid-1,v+r-mid+1),//处理左边
    DivideAndSolve(mid+1,r,v+1);//处理右边
}
int main()
{
    freopen("Shion5.out","w",stdout);
    register int i;cout<<N<<endl;
    for(DivideAndSolve(1,N,0),i=1;i<=N;++i) cout<<ans[i]<<endl;//构造,然后输出
    return 0;
}

子任务\(6\):\(Bogo\)排序\(AC\),快速排序\(TLE\)

毫无疑问,是本题当中最恶心的一个子任务。

首先,不难想到,这题的随机肯定有某种规律。

于是便想到把这些数字一个个扔进计算器里看其二进制下的值,于是发现\(RNG\_a\)和\(RNG\_b\)着两个参数差不多在模一个不太大的\(2\)的幂的情况下余\(1\)。

所以,它的交换数据这一部分的代码中的\(j\),其实就等于\((seed+i+1)\%n\)。

然后经过一定的测试,可得\(n=4096\)。

由于我们需要在一次之内能够排完序,所以我们倒序处理,就可以构造出原序列。

而我们需要使构造出的序列能卡掉快排,试得\(seed=2048\)。

但是,\(seed\)是要根据\(a_i\)这个序列来算出的,而不是你随便就能定的。

比较容易想到先求出前\(n-1\)个数使\(seed\)得到的值,然后找到一个合适的\(a_n\)使得\(seed\)能够恰好等于\(2048\),但这就需要枚举。

可这个序列的相对大小又不能改变,因此我们枚举范围十分小。

则可以考虑将已经构造出的序列中大于\(a_n\)的数全部加上一个很大的数,这样就能使得相对大小不变。然后再在这一较大的值域中进行枚举,就能找到了。

造数据代码如下:

#include<bits/stdc++.h>
#define N 4096
#define uint unsigned int
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int a[N+5];
int main()
{
    freopen("Shion6.out","w",stdout);
    register int i;register uint seed;
    for(i=1;i<=N;++i) a[i]=i;for(i=N;i;--i) swap(a[i],a[(i+2048)%N+1]);//倒序处理,构造出原数列
    for(i=1;i<=N;++i) a[i]>a[N]&&(a[i]+=1000000);//将已经构造出的序列中大于a[n]的数全部加上一个很大的数,这样就能使得相对大小不变
    for(seed=2166136261,i=1;i^N;++i) seed=(seed*16777619)^a[i];//求出前n-1个数使seed得到的值
    for(i=4092;i<=1004093;++i) if(((seed*16777619)^i)%N==2048) {a[N]=i;break;}//枚举一个合适的a[n]使得seed能够恰好等于2048
    for(cout<<N<<endl,i=1;i<=N;++i) cout<<a[i]<<endl;//输出答案
    return 0;
}

附录:答案

以上就是这道题的全部解题思路了。

点击下载答案

原文地址:https://www.cnblogs.com/chenxiaoran666/p/UOJ83.html

时间: 2024-10-13 10:17:15

【UOJ83】【UR #7】水题出题人(提交答案题)的相关文章

提交答案题(网络流)

提交答案题(网络流) 计算一次函数的值实在是太难了,所以为了简化问题,我们计算二次函数.现在给了你\(N\)个二次函数,第\(i\)个二次函数的形状为\(f_i(x_i) = a_i {x_i}^2 + b_i x_i + c_i , l_i \leq x_i \leq r_i\).同时我们又给了你\(M\)个限制关系,限制关系的形式为\(x_u \leq x_v + d\).现在告诉你所有这些信息,你需要最大化\(\sum_{i = 1}^{N} f_i(x_i)\)的值. \(1 \leq

一道超级坑爹的水题(ACdream oj 无耻的出题人)

 A - 无耻的出题人 Time Limit: 2000/1000 MS (Java/Others)      Memory Limit: 65536/32768 KB (Java/Others) Submit Status Problem Description 听到X神要参加比赛,只会Fibnacci数的出题人被吓得哭晕在厕所.为了防止X神AK(ALL KILL)比赛题目,无耻的出题人只好在题面上做些手脚(加密).其中一道题的题目描述如下: hjxh dwh v vxxpde,mmo i

虐我半下午的水题(ACdream无耻的出题人)

对于这道题我只想说坑爹.绝对的坑爹. 还有这样出题的..... 无耻的出题人 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 KB (Java/Others) SubmitStatistic Next Problem Problem Description 听到X神要参加比赛,只会Fibnacci数的出题人被吓得哭晕在厕所.为了防止X神AK(ALL KILL)比赛题目,无耻的出题人只好在题面上做些手脚(加密).其中

bzoj-3203 保护出题人

题意: 在一个诡异的植物大战僵尸游戏中,给出n关: 第i关队首僵尸距房门xi,两个僵尸之间间隔为d: 每次在队首添加一个血量为ai的僵尸,其他僵尸不变: 每关在门前放一个攻击力任意的植物,求n关放置植物总攻击力的最小值: n<=100000,其他数据<=10^12: 题解: 题意叙述略诡异..建议还是去看一眼原题: 首先考虑对于每一关的答案,应该是恰好将最难打死的僵尸打死的攻击力值: 令s[i]为i这个僵尸血量与它前面僵尸血量之和,dis[i]为这个僵尸距房门的距离: 那么答案就是ans=ma

如果是壬癸水八字的人呢?壬癸水遇风水-日课-命理-养生-日课原理与择日玄机命理养生

紧着我又接到另一个老板的电话,电话的内容是约请我晚上一起吃饭,因为他过来的时辰不好,我就让他先到我办公室坐了一会,等过了这个申时到酉时的时候再下去,因为一时分八刻,在酉时的前两刻还有申时的过度内容.所以我们在算八字的时候,要知道他的出生地和现居住,尽量要知道求测者的出生时间是几点几分,因为一个时辰要分为八刻,每一刻代表的度数也不一样,在一.二刻和七 八刻的时候就要两边跨,我们就要看他的大运是几岁运,如果是昨天立秋,今天出生的人,他是几岁运?他是逆推运必然是1虚岁岁运,如果求测者出生的时辰是在午月

SDOI2013 保护出题人

Description ?出题人铭铭认为给SDOI2012 出题太可怕了,因为总要被骂,于是他又给SDOI2013 出题了. 参加SDOI2012 的小朋友们释放出大量的僵尸,企图攻击铭铭的家.而你作为SDOI2013的参赛者,你需要保护出题人铭铭. 僵尸从唯一一条笔直道路接近,你们需要在铭铭的房门前放置植物攻击僵尸,避免僵尸碰到房子.第一关,一只血量为a1 点的僵尸从距离房子x1 米处匀速接近,你们放置了攻击力为y1 点/秒的植物进行防御:第二关,在上一关基础上,僵尸队列排头增加一只血量为a2

hdu 1234 开门人和关门人(简单题)

开门人和关门人 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 10658    Accepted Submission(s): 5434 Problem Description 每天第一个到机房的人要把门打开,最后一个离开的人要把门关好.现有一堆杂乱的机房签 到.签离记录,请根据记录找出当天开门和关门的人. Input 测试输入的第一行

bzoj3203: [Sdoi2013]保护出题人 凸包+三分

/************************************************************** Problem: 3203 User: wangyucheng Language: C++ Result: Accepted Time:344 ms Memory:4396 kb ****************************************************************/ #include<iostream> #include&l

【BZOJ3203】[Sdoi2013]保护出题人 二分+凸包

[BZOJ3203][Sdoi2013]保护出题人 Description Input 第一行两个空格隔开的正整数n和d,分别表示关数和相邻僵尸间的距离.接下来n行每行两个空格隔开的正整数,第i + 1行为Ai和 Xi,分别表示相比上一关在僵尸队列排头增加血量为Ai 点的僵尸,排头僵尸从距离房子Xi米处开始接近. Output 一个数,n关植物攻击力的最小总和 ,保留到整数. Sample Input 5 2 3 3 1 1 10 8 4 8 2 3 Sample Output 7 HINT 第