• P1338 末日的传说[水题]


    题目描述

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

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

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

    1, 2, 3, … N

    第二天,日历自动变为

    1, 2, 3, … N, N-1

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

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

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

    解析

    发现窝模拟有大问题,这道题就是个简单的贪心,结果窝思路清晰依然写不出。。。

    题目要求我们输出一个长度为(n)的含有(m)个逆序对的字典序最小的序列。

    众所周知,一个长度为(n)的序列最多贡献(n(n-1)/2)个逆序对,因此当我们把某个数(i)放到序列末尾时,除了之前在这个数后面的数会与它形成逆序对之外,还会最多产生((n-i)(n-i-1)/2)个逆序对。我们只需贪心地每次选择最靠后的能使产生的逆序对小于(m)个数放到序列末尾,就可以产生一个符合要求的序列。

    一开始本来想写递归的,发现我太蒻,竟然不会写。。。(太菜了

    参考代码

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define ll long long
    #define N 50010
    using namespace std;
    ll n,m,ans[N];
    int main()
    {
    	scanf("%lld%lld",&n,&m);
    	ll s=1,e=n;
    	for(int i=1;i<=n;++i){
    		ll t=(ll)(n-i)*(n-i-1)/2;
    		if(t>=m) ans[s++]=i;
    		else ans[e--]=i,m-=(e-s+1);
    	}
    	for(int i=1;i<=n;++i) printf("%lld ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    bzoj 3040: 最短路(road)
    bzoj 2049: [Sdoi2008]Cave 洞穴勘测
    poj 2505 A multiplication game
    hdu 1729 Stone Game
    经典博弈模型
    hdu 1848 Fibonacci again and again(SG函数)
    hdu 2147 kiki's game(巴什博弈)
    hdu 1847 Good Luck in CET-4 Everybody!(巴什博弈)
    hdu 4388 Stone Game II
    poj 2234 Matches Game
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11344866.html
Copyright © 2020-2023  润新知