zoukankan      html  css  js  c++  java
  • 末日传说

    Description

    只要是参加jsoi活动的同学一定都听说过Hanoi塔的传说:三根柱子上的金片每天被移动一次,当所有的金片都被移完之后,世界末日也就随之降临了。

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

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

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

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

    日子一天一天地过着。有一天,一位预言者出现了——他预言道,当这个日历到达某个上帝安排的时刻,这个世界就会崩溃……他还预言到,假如某一个日期的逆序达到一个值M的时候,世界末日就要降临。

    什么是逆序?日历中的两个不同符号,假如排在前面的那个比排在后面的那个更大,就是一个逆序,一个日期的逆序总数达到M后,末日就要降临,人们都期待一个贤者,能够预见那一天,到底将在什么时候到来?

    Analysis

    首先通过递推算出i个数字排列的最大逆序对数dp[i],很容易想到n个数M个逆序对的列首为M-dp[n-1]+1(当然要是M<=dp[n-1]就是1),接着递归产生子问题,只需要将每次算出的列首转换为真实值即可(即之前输出过的不能再输出)。

    对于这唯一的难点,显然可以用树状数组。

    Code

    #include <bits/stdc++.h>
    const int N=50000;
    int dp[N+10];
    class Binary_index_tree{
    	private:
    		int s[N+10];
    		int lowbit(int i){
    			return i&(-i);
    		}
    	public:
    		int n;
    		void Init(){
    			for(int i=1;i<=n;i++)
    				Modify(i);
    		}
    		void Modify(int i){
    			if(i>n)return;
    			s[i]+=1;
    			Modify(i+lowbit(i));
    		}
    		int Query(int i){
    			if(!i)return 0;
    			return s[i]+Query(i-lowbit(i));
    		}
    }tree;
    void pretreat(){
    	dp[1]=0;
    	for(int i=2;i<=N;i++)
    		dp[i]=dp[i-1]+i-1;
    }
    void search(int n,int m){
    	if(!n)return;
    	int k=m<=dp[n-1]?1:m-dp[n-1]+1;
    	printf("%d ",tree.Query(k));
    	tree.Modify(k);
    	search(n-1,std::min(dp[n-1],m));
    }
    int main(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	pretreat();
    	tree.n=n;
    	tree.Init();
    	search(n,m);
    	return 0;
    }
    
  • 相关阅读:
    识人高招:六招看清一个人
    让工作变简单的10种技巧
    推销中的五大提问技巧
    给初次签约大学生的忠告
    只要有钱50岁男人也嫁
    我老公一个月赚15000,但是幸福在哪呢?(转载)
    想法简单,生命更宽!
    中国经典到吐血的谎话
    你的思想是你最大的敌人
    完美人生从哪里起步
  • 原文地址:https://www.cnblogs.com/qswx/p/9644077.html
Copyright © 2011-2022 走看看