洛谷P1338 末日的传说 题解

P1338 末日的传说 题解

吐槽

题目描述:https://www.luogu.com.cn/problem/P1338

其实,我今天本来是想去写一下代码难度不高,但是比较需要思考的题目
(本来是想说比较需要智力的题目,想了想,算了......)

言归正传,这道题目,看完之后,我其实有点懵(N+1进制?!!),题面:

1, 2, 3, … N
第二天,日历自动变为
1, 2, 3, … N, N-1
……每次它都生成一个以前未出现过的“最小”的排列——把它转为N+1进制后数的数值最小。

我在想:N+1进制??什么鬼......
再认真看一下样例,才发现:逆序才是有用的.....

他还预言到,假如某一个日期的逆序达到一个值M的时候
(这就是答案了<_<)

OK,那么,逆序大家都应该是了解的,应为题面上面有。

假如排在前面的那个比排在后面的那个更大,就是一个逆序

好的,吐槽完毕,现在:

题目分析

  • 1、问题规模
  • 2、可实现的方法
  • 3、正解&分析

1.问题规模

对于10%的数据有N <= 10。
对于40%的数据有N <= 1000。
对于100%的数据有 N <= 50000
所有数据均有解。

N<=50000,明显,O(N\(^2\))解决不了,所以试试O(N\(log n\))O(N)能否解决
PS:O(1)的......说实话,讲道理不存在......

看题目,就会很自然的想:线性复杂度能不能解决?(O(N))

不急,先看。

2.可实现的方法

  • 暴力(枚举全排列)
  • 神奇思路

很明显,枚举全排列暴力的做法,代码量不多,而且不难(无论是手写还是用next_permutation()),但是,你可能只能过1~4个点。

代码的话......我比较懒,没打,就不贴了......

现在,关键点到了:如何用一种思路把问题简化线性复杂度
假设,你是一家公司的董事长,你要做一个项目,但是你不想做所有的步骤,怎么办??很简单,你做其中的一步,然后把它交给你的秘书,让他(她,或者......它<_<)帮你完成剩下所有的,然后你做完最后一步之后,宣布:项目完成,然后向投资方交差,然后继续......
OK,那么,如果你是秘书,你该干什么?(这个问题,排除对“它”的询问)很简单,你学习一下你的老板,把问题分解,交给下属,然后这样一步一步下去,再传上来,一人做一步,最后,完美finish。
递归的思路,线性的复杂度(对于本题))

现在,我们来分解一下问题,显然,假设你现在正在处理第i个数,前面都处理完了,现在还需要now个逆序,你该如何解决??

解决前,我们应该了解一下:1、2、3~n个数,逆序最多有多少个

最少的逆序,很显然:1、2、3、······n 总共0个
做多:n、n-1、······1 总共多少个?
现在就是本问题用到的第一个(貌似也是最后一个)数学公式
高斯求和
对于第1个数,它的逆序个数:n-1 (从第n-1到1)
对于第2个数,它的逆序个数:n-2 (从第n-2到1)
······
第n-1个数,它的逆序个数:1
第n个数,它的逆序个数:0

归纳:逆序和(n-1+1)\(*\)(n-1)\(/\) 2=(n-1)\(*\)(n-1)\(/\) 2
推论:对于第i个数,i及i以后所有数的最大逆序个数和(倒叙):
((n-i)\(*\)(n-i-1)$/$2)
所以:用递归的思路,对于第i个数,你只要判断它放在哪,于是,神奇思路:

因为:假设逆序数相同,要求字典序最小
所以:越小的数向前放越好
所以:一层层用这种贪心思路放下去,可以保证最优解

所以核心思想
假设对于第i个数的可造成的最大逆序数:((n-i)\(*\)(n-i-1)$/$2)
设这个为S
如果这个S>=now(目前需要的逆序数个数)
就放在目前数组(ans数组)的开头

  • PS:为什么等于也放?这个大家想一想就知道了,我懒得码字证明了,对不起啦!!!

反之,就放在末尾。(为了防止出现后面所有数倒叙都不成立的情况)

OK,题目分析结束,代码如下(仅供参考。。。)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <iostream>
using namespace std;

#define ll long long
#define mx(x,y) x>y?x:y
#define mn(x,y) x<y?x:y
#define _e putchar(' ')
#define _v putchar('\n')
#define BF_N 1<<17

char buf[BF_N],sl,sr;
FILE *fin;
char gc() {
	return sl==sr&&(sr=(sl=0)+fread(buf,1,BF_N,stdin),sl==sr)?EOF:buf[sl++];
}
int Read() {
	int ans=0,k=1;
	char c=gc();
	while(!isdigit(c)) {
		if(c=='-')k=-1;
		c=gc();
	}
	while(isdigit(c)) {
		ans=ans*10+c-'0';
		c=gc();
	}
	return k*ans;
}

long long n,m,fi=1,la;
long long ans[1000005];

void init(){
	scanf ("%lld%lld",&n,&m);
	la=n;
	return;
}

#define he(i) ((n-i)*(n-i-1)/2)

void work(){
	long long now=m;
	for (long long i=1;i<=n;i++){
		if (he(i)>=now)
			ans[fi++]=i;
		else
			ans[la--]=i,now-=(n-i);
	}
	for (int i=1;i<=n;i++)
		printf ("%lld ",ans[i]);
	return;
}

int main(){
	init();
	work();
	return 0;
}


各位自己体会一下啊,我累了,溜了!!

哦哦哦,对了,我在这边道歉一下:我不小心把题目分析中的2和3搞到一起了,抱歉啊QwQ

Over,Thanks。

posted @ 2021-01-21 00:26  fallingdust  阅读(95)  评论(0编辑  收藏  举报