Wiki 2746(末日传说-打表找字典序最小逆序对=m的序列)

    在古老的东方,人们都采用一种奇妙的方式记录日期:他们用一些特殊的符号来表示从1开始的连续整数,1表示最小而N表示最大。创世纪的第一天,日历就被赋予了生命,它自动的开始计数,就像排列在不断地增加。

    我们用1——N来表示日历的元素,第一天的日历是:1,2,3,……N-1,N

                                                  第二天,日历自动变为1,2,3,……N,N-1

    每次他都生成一个以前从未出现过的“最小”的排列——将它转为(N+1)进制后数的数值最小。

    有一天,一个预言者出现了——虽然没人让他出现——他预言道,当这个日历到达某个上帝安排的时刻(……),这个世界就会崩溃……他还预言,假如一个某一个日子的逆序到达一个指定数值M时,世界末日将来临。

    逆序是什么?日历中两个不同的符号,假如排在前面的那个比后面的那个大,就是一个逆序(不一定相邻)。人们期待一个贤者来预见那一天。

只包含一行两个正整数,分别为N和M。

输出一行,为世界末日的日期,每个数字间用一个空格隔开。

5 4

1 3 5 4 2

对于10%的数据有N<=10。

对于40%的数据有N<=1000。

对于100%的数据有N<=50000。

所有数据均有解。

为避免出现错误,请在程序最后加上一个 writeln;


这题又是打表找规律。。。

我换了和FJ省选二试一样的错误。。。又手推公式。。结果各种推错。。果然这种题目不打表都不行。。

好了,我写了一个简洁的dabiao程序:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Lson (x<<1)
#define Rson ((x<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define MAXN (50000+10)
long long mul(long long a,long long b){return (a*b)%F;}
long long add(long long a,long long b){return (a+b)%F;}
long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;}
typedef long long ll;
int n,a[MAXN];
bool b[MAXN]={0};
int main()
{
	n=5;
	For(i,n) a[i]=i;
	For(i,5*4*3*2)
	{
		int tot=0;
		For(j,n) Fork(k,j+1,n) if (a[j]>a[k]) tot++;
		if (!b[tot]) {For(i,n) cout<<a[i]<<' ';cout<<':'<<tot<<endl;b[tot]=1;}
		next_permutation(a+1,a+1+n);		
	}	
	return 0;
}

差不多就是这样。。。

1 2 3 4 5 :0
1 2 3 5 4 :1
1 2 4 5 3 :2
1 2 5 4 3 :3
1 3 5 4 2 :4
1 4 5 3 2 :5
1 5 4 3 2 :6
2 5 4 3 1 :7
3 5 4 2 1 :8
4 5 3 2 1 :9
5 4 3 2 1 :10

不管你们发现没,我是发现了。。

差不多规律是这样——后一段反序,然后一个数按顺序从小到大提到前面,其它不变(仍反序)。。

PS:反序就是 5 4 2 1 这样的单调递减序列

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork2(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Lson (x<<1)
#define Rson ((x<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define MAXN (50000+10)
long long mul(long long a,long long b){return (a*b)%F;}
long long add(long long a,long long b){return (a+b)%F;}
long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;}
typedef long long ll;
int n,m;
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin>>n>>m;
	int p=0;
	while (p*(p+1)/2<=m) p++;
	For(i,n-p-1) cout<<i<<' ';
	int sum=p*(p-1)/2;
	cout<<n-p+(m-sum);
	//if (sum==m)
	{
		int t=n;
		Fork2(i,n-p+1,n) 
		{
			while (t==n-p+m-sum) t--;
			cout<<' '<<t--;
		}
	}
	puts("");
	return 0;
}






你可能感兴趣的:(Wiki 2746(末日传说-打表找字典序最小逆序对=m的序列))