Codeforeces 501D 平衡树求排名和第k大

传送门:点击打开链接

D. Misha and Permutations Summation
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Let's define the sum of two permutations p andq of numbers0, 1, ..., (n - 1) as permutation, wherePerm(x) is the x-th lexicographically permutation of numbers0, 1, ..., (n - 1) (counting from zero), andOrd(p) is the number of permutationp in the lexicographical order.

For example, Perm(0) = (0, 1, ..., n - 2, n - 1),Perm(n! - 1) = (n - 1, n - 2, ..., 1, 0)

Misha has two permutations, p and q. Your task is to find their sum.

Permutation a = (a0, a1, ..., an - 1) is called to be lexicographically smaller than permutation b = (b0, b1, ..., bn - 1), if for some k following conditions hold: a0 = b0, a1 = b1, ..., ak - 1 = bk - 1, ak < bk.

Input

The first line contains an integer n (1 ≤ n ≤ 200 000).

The second line contains n distinct integers from0 ton - 1, separated by a space, forming permutationp.

The third line contains n distinct integers from0 ton - 1, separated by spaces, forming permutationq.

Output

Print n distinct integers from 0 to n - 1, forming the sum of the given permutations. Separate the numbers by spaces.

Sample test(s)
Input
2
0 1
0 1
Output
0 1
Input
2
0 1
1 0
Output
1 0
Input
3
1 2 0
2 1 0
Output
1 0 2
Note

Permutations of numbers from 0 to 1 in the lexicographical order: (0, 1), (1, 0).

In the first sample Ord(p) = 0 andOrd(q) = 0, so the answer is.

In the second sample Ord(p) = 0 andOrd(q) = 1, so the answer is.

Permutations of numbers from 0 to 2 in the lexicographical order: (0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0).

In the third sample Ord(p) = 3 andOrd(q) = 5, so the answer is.

题意:n(2*10^5)个元素的排列有n!种  用Perm(x)表示字典序第x的序列(从0开始)  用Ord(排列y)表示排列y的字典序  现在输入排列p和q  求  Perm([Ord(p)+Ord(q)]%n!)

思路:对于第i位p[i]  如果它是后面的n-i+1个数中第d小的数字  那么说明比它小的d-1个数字所产生的全排列都已经计数过了。由于阶乘太大,而我们感兴趣的只是那个d-1的值,所以我们只记录每一位的那个d-1的值,最好按照类似高精度的方法,得到(Ord(p)+Ord(q))%n!。再用上述方法的逆方法,得出Perm即可,由求排名变成了求第K大。

代码:(内含有treap模板)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
#define LL long long
#define maxn 200005
class treap //treap是一颗随机的二叉排序树,可以实现插入,删除,询问前驱、后驱、排名等功能,不支持负数。各功能的时间复杂度为O(logn)
{
	struct node //treap的节点定义
	{
		node* ch[2];
		int v, r, s;
		int cmp(int x)
		{
			if (x == v) return -1;
			return(x < v ? 0 : 1);
		}
	};
private:
	node* root; //treap的根
	void updata(node* o)
	{
		if (!o) return;
		o->s = 1;
		if (o->ch[0]) o->s += o->ch[0]->s;
		if (o->ch[1]) o->s += o->ch[1]->s;
	}
	void rateto(node* &o, int d)
	{
		node* k;
		k = o->ch[d ^ 1];
		o->ch[d ^ 1] = k->ch[d];
		k->ch[d] = o;
		updata(o);
		updata(k);
		o = k;
	}
	int left(node* o)
	{
		if (!o) return 0;
		if (o->ch[0]) return o->ch[0]->s;
		else return 0;
	}
	void mymemory(node* &o)
	{
		if (!o) return;
		if (o->ch[0]) mymemory(o->ch[0]);
		if (o->ch[1]) mymemory(o->ch[1]);
		free(o);
		o = 0;
	}
	void add(node* &o, int x)
	{
		if (!o)
		{
			o = new node();
			o->ch[0] = o->ch[1] = 0;
			o->v = x;
			o->r = rand()*rand();
			o->s = 1;
			return;
		}
		int d = o->cmp(x);
		if (d == -1) return;
		else
		{
			add(o->ch[d], x);
			updata(o);
			if (o->r < o->ch[d]->r) rateto(o, d ^ 1);
		}
	}
	void remove(node* &o, int x)
	{
		if (!o) return;
		int d = o->cmp(x);
		if (d == -1)
		{
			if (!(o->ch[0]))
			{
				node *k = o->ch[1];
				free(o);
				o = k;
			}
			else if (!(o->ch[1]))
			{
				node *k = o->ch[0];
				free(o);
				o = k;
			}
			else
			{
				int d2 = o->ch[0]->r > o->ch[1]->r ? 1 : 0;
				rateto(o, d2);
				remove(o->ch[d2], x);
			}
		}
		else remove(o->ch[d], x);
		updata(o);
	}
	bool finded(node* o, int x)
	{
		if (!o) return 0;
		while (o)
		{
			int d = o->cmp(x);
			if (d == -1) return 1;
			else o = o->ch[d];
		}
		return 0;
	}
	int Kth(node* o, int k)
	{
		if (!o) return -1;
		if (k == left(o) + 1) return o->v;
		else
		{
			if (k <= left(o)) return Kth(o->ch[0], k);
			else return Kth(o->ch[1], k - left(o) - 1);
		}
	}
	int Rank(node* o, int x, int n)
	{
		if (!o) return -1;
		int d = o->cmp(x);
		if (d == -1) return n + left(o) + 1;
		if (d == 0) return Rank(o->ch[0], x, n);
		else return Rank(o->ch[1], x, n + left(o) + 1);
	}
	int Suc(node* &o, int x)
	{
		if (!o) return -1;
		int d = o->cmp(x);
		if (d == -1)
		{
			if (o->ch[1]) return Suc(o->ch[1], x);
			else return -1;
		}
		else
		{
			int s = Suc(o->ch[d], x);
			if (s != -1) return s;
			if (s == -1 && o->v > x) return o->v;
			else return -1;
		}
	}
	int Pre(node* &o, int x)
	{
		if (!o) return -1;
		int d = o->cmp(x);
		if (d == -1)
		{
			if (o->ch[0]) return Pre(o->ch[0], x);
			else return -1;
		}
		else
		{
			int s = Pre(o->ch[d], x);
			if (s != -1) return s;
			if (s == -1 && o->v < x) return o->v;
			else return -1;
		}
	}
	//以上为treap的功能实现部分
public:
	int size()//返回该treap的内元素的个数
	{
		if (root) return root->s;
		else return 0;
	}
	void insert(int x)//向treap中插元素x,如果x已经在treap中,则不操作
	{
		add(root, x);
	}
	void erase(int x)//删除treap中元素x,如果没有,则不操作
	{
		remove(root, x);
	}
	bool find(int x)//返回x是否在treap中
	{
		return finded(root, x);
	}
	int kth(int x)//返treap中的排名为x的元素的值,如果x非法,返回-1
	{
		if (x <= 0 || x > size()) return -1;
		return Kth(root, x);
	}
	int rank(int x)//返回x在treap中的排名,如果x不在treap中,返回-1
	{
		return Rank(root, x, 0);
	}
	void clear()//清空treap,释放内存
	{
		mymemory(root);
	}
	int  suc(int x)//返回treap中比x大的第一元素的值(后驱),如果x大于等于treap中的最大值,返回-1
	{
		return Suc(root, x);
	}
	int pre(int x)//返回treap中比x小的第一元素的值(前驱),如果x小于等于treap中的最小值,返回-1
	{
		return Pre(root, x);
	}
	treap()//定义初始化;
	{
		root = 0;
	}
};
int a[maxn],b[maxn];
int p[maxn],q[maxn];
int c[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    treap ta,tb;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        ta.insert(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        tb.insert(b[i]);
    }
    for(int i=1;i<=n;i++)
    {
        p[i]=ta.rank(a[i])-1;
        ta.erase(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        q[i]=tb.rank(b[i])-1;
        tb.erase(b[i]);
    }
    memset(c,0,sizeof(c));
    for(int i=n;i>=1;i--)
    {
        c[i]+=q[i]+p[i];
        if(c[i]>=n-i+1)
        {
            c[i]-=n-i+1;
            c[i-1]++;
        }
    }
    treap tc;
    for(int i=0;i<n;i++) tc.insert(i);
    for(int i=1;i<=n;i++)
    {
        int tmp=tc.kth(c[i]+1);
        printf("%d ",tmp);
        tc.erase(tmp);
    }
    printf("\n");
    return 0;
}

其实这道题用线段树也能做,输入的数字不大,不需要离散化都行

#include<cstdio>
#include<cstring>
#include<cstdlib>
#define maxn 200005
using namespace std;
class segement_tree_for_balance
{
private:
    struct node
    {
        int l,r,sum;
    };
    node *tree;
    inline void pushup(int id)
    {
        tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
    }
    void build(int id,int l,int r)
    {
        tree[id].l=l;
        tree[id].r=r;
        tree[id].sum=0;
        if(l!=r)
        {
            int mid=(l+r)>>1;
            build(id<<1,l,mid);
            build(id<<1|1,mid+1,r);
        }
    }
    int find_num_front(int id,int x)
    {
        if(tree[id].l==tree[id].r) return 0;
        else
        {
            int mid=(tree[id].l+tree[id].r)>>1;
            if(x<=mid) return find_num_front(id<<1,x);
            else return tree[id<<1].sum+find_num_front(id<<1|1,x);
        }
    }
    int find_kth_num(int id,int x)
    {
        if(tree[id].l==tree[id].r) return tree[id].l;
        else
        {
            if(x<=tree[id<<1].sum) return find_kth_num(id<<1,x);
            else return find_kth_num(id<<1|1,x-tree[id<<1].sum);
        }
    }
    void update(int id,int pos,int x)
    {
        if(tree[id].l==tree[id].r) tree[id].sum+=x;
        else
        {
            int mid=(tree[id].l+tree[id].r)>>1;
            if(pos<=mid) update(id<<1,pos,x);
            else update(id<<1|1,pos,x);
            pushup(id);
        }
    }
public:
    segement_tree_for_balance(int size)
    {
        tree=(node*)malloc(4*size*sizeof(node));
        build(1,0,size-1);
    }
    void insert(int x)
    {
        update(1,x,1);
    }
    void erase(int x)
    {
        update(1,x,-1);
    }
    int kth(int k)
    {
        return find_kth_num(1,k);
    }
    int rank(int x)
    {
        return find_num_front(1,x)+1;
    }
};
int a[maxn],b[maxn];
int p[maxn],q[maxn];
int c[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    segement_tree_for_balance ta(n),tb(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        ta.insert(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        tb.insert(b[i]);
    }
    for(int i=1;i<=n;i++)
    {
        p[i]=ta.rank(a[i])-1;
        ta.erase(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        q[i]=tb.rank(b[i])-1;
        tb.erase(b[i]);
    }
    memset(c,0,sizeof(c));
    for(int i=n;i>=1;i--)
    {
        c[i]+=q[i]+p[i];
        if(c[i]>=n-i+1)
        {
            c[i]-=n-i+1;
            c[i-1]++;
        }
    }
    segement_tree_for_balance tc(n);
    for(int i=0;i<n;i++) tc.insert(i);
    for(int i=1;i<=n;i++)
    {
        int tmp=tc.kth(c[i]+1);
        printf("%d ",tmp);
        tc.erase(tmp);
    }
    printf("\n");
    return 0;
}
比平衡树快了近1倍


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子。为了使cnt_white - cnt_black尽可能大,可以使用两次形动态规划来解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的链所代表的子。在第二次遍历中,需要维护一个sum变量,用于存储链所代表的子的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是链所代表的子的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个形动态规划问题。在这个问题中,需要解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的链所代表的子。在第二次遍历中,需要维护一个sum变量,用于存储链所代表的子的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值