HDU 5338 ZZX and Permutations

ZZX and Permutations

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 827    Accepted Submission(s): 259


Problem Description
ZZX likes permutations.

ZZX knows that a permutation can be decomposed into disjoint cycles(see https://en.wikipedia.org/wiki/Permutation#Cycle_notation). For example:
145632=(1)(35)(462)=(462)(1)(35)=(35)(1)(462)=(246)(1)(53)=(624)(1)(53)……
Note that there are many ways to rewrite it, but they are all equivalent.
A cycle with only one element is also written in the decomposition, like (1) in the example above.

Now, we remove all the parentheses in the decomposition. So the decomposition of 145632 can be 135462,462135,351462,246153,624153……

Now you are given the decomposition of a permutation after removing all the parentheses (itself is also a permutation). You should recover the original permutation. There are many ways to recover, so you should find the one with largest lexicographic order.
 

Input
First line contains an integer  t, the number of test cases.
Then  t testcases follow. In each testcase:
First line contains an integer  n, the size of the permutation.
Second line contains  n space-separated integers, the decomposition after removing parentheses.

n105. There are 10 testcases satisfying  n105, 200 testcases satisfying  n1000.
 

Output
Output  n space-separated numbers in a line for each testcase.
Don't output space after the last number of a line.
 

Sample Input
 
    
2 6 1 4 5 6 3 2 2 1 2
 

Sample Output
 
    
4 6 2 5 1 3 2 1
 

Author
XJZX
 

Source
2015 Multi-University Training Contest 4
 

Recommend
wange2014   |   We have carefully selected several similar problems for you:   5368  5367  5366  5365  5364 

 




多校的一道题,题意特别绕,方法不难。
题意就是给出一串数,你可以把它们分成任意个单独的不相交循环( disjoint cycles),题中的第一个样例就是分成了(1 4 5)(6 3 2),分了之后对其进行重组,重组的规则就是按照循环的,比如(1 4 5)就是第1个位置放4,第4个位置放5,第5个位置放1,后面那个类似。问题是问你如何得到一个字典序最大的串。

这题可以任意划分,要字典序最大的串,那么靠前位置的数字应该尽量大。以第一个位置为例,根据观察可以得出第一个位置能放的数字应该是原串中位于1的左边的任何一个数和1右边的那个数。比如54123,那么划分之后第一个位置可能放5,4,和2,划分方法分别是(541)(23)那么第一个数就是5,如果是(5)(41)(23)第一个数就是4,如果是(54)(12)(3)第一个数就是2。得到了这个结论,我们就可以从1开始,在左边的所有位置和右边一位中寻找最大的数放在那个位置。
但是要注意这题中n有10e5,用模拟会超时,所以应该用线段树进行查询,还有要用set存储和查找。



代码:

#include 
#include 
#include 
#include 

#define max(a,b) a>b?a:b
#define min(a,b) a>b?b:a
#define N 100005

#define lson l,m,root<<1
#define rson m+1,r,root<<1|1

using namespace std;
int n,t;
int a[N];
int sx[N];
int v[N<<2];
int w[N];
int vv[N];

int ma[N<<2];

void pushup(int root)
{
    ma[root]=max(ma[root<<1],ma[root<<1|1]);
}

void build(int l,int r,int root)
{
    if(l==r)
    {
        ma[root]=a[l];
        return ;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(root);
}

void update(int wz,int val,int l,int r,int root)
{
    int m=(l+r)>>1;
    if(l==r)
    {
        ma[root]=val;
        return ;
    }
    if(wz<=m)
    {
        update(wz,val,lson);
    }
    else
    {
        update(wz,val,rson);
    }
    pushup(root);
}

int query(int st,int en,int l,int r,int root)
{
    int m=(l+r)>>1;
    
    int ans=-1;
    if(st==l&&r==en)
    {
        return ma[root];
    }
    else
    {
        if(en<=m)
        {
            ans=query(st,en,lson);
        }
        else if(st>m)
        {
            ans=query(st,en,rson);
        }
        else
        {
            ans=max(query(st,m,lson),query(m+1,en,rson));
        }
        
    }
    return ans;
    
}


int main()
{
    scanf("%d",&t);
    while(t--)
    {
        setst;//set存的是已确定的数,就是已经被括起来的数。
        memset(a,0,sizeof(a));
        memset(sx,0,sizeof(sx));
        memset(w,0,sizeof(w));
        memset(v,0,sizeof(v));
        memset(ma,-1,sizeof(ma));
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            w[a[i]]=i;//w[i]记录了i在原串的位置。
        }
        build(1,n,1);
        for(int i=1; i<=n; i++)
        {
            if(sx[i]!=0)continue;//sx[i]用来存放最终的顺序,开始为0,如果不为0则已确定此位置。
            int l=1;
            if(!st.empty())//如果非空说明已经有括号了。
            {
                set::iterator temp=st.lower_bound(w[i]);
//这个set::iterator是容器里的指针,c++好像可以用auto,这里的作用是如果非空找出容器里不小于i的位置的第一个数。
                if(temp!=st.begin())//如果temp和set中的开头元素不一样,说明w[i]前面已经有元素被括起来了,那么就从那个后一个开始。否则就从1开始。
                {
                    temp--;
                    l=(*temp)+1;
                }
            }
            int maxv=query(l,w[i],1,n,1);//找出区间l到w[i]的最大值。
            if(maxv


 
   


你可能感兴趣的:(HDU 5338 ZZX and Permutations)