zoukankan      html  css  js  c++  java
  • 【LOJ2055】【洛谷P2824】排序【线段树】【二分答案】

    题目大意:

    题目链接:
    洛谷:https://www.luogu.org/problem/P2824
    LOJ:https://loj.ac/problem/2055#submit_code
    给出一个1n1sim n的排列,两个操作:

    • 0 l r0 l r:将[l,r][l,r]的数字升序排列
    • 1 l r1 l r:将[l,r][l,r]的数字降序排列

    给出qq,求出最终位于数列第qq位的数字。


    思路:

    nbnb思路!
    暴力的复杂度时O(nmlogn)O(nmlog n)的,其中排序复杂度O(nlogn)O(nlog n),如果可以有效减少排序的复杂度,就可以降低代码复杂度。
    我们发现,01序列的排序可以在O(logn)O(log n)内完成。以升序排列为例,我们用线段树维护出区间[l,r][l,r]数字1的个数sumsum,然后区间修改[l,rsum+1][l,r-sum+1]为1,[l,l+sum][l,l+sum]为0。 .
    那么如何把原数列转换成01数列呢?考虑二分答案。
    我们在区间[1,n][1,n]内二分答案midmid,每次把大于等于midmid的数字变成1,小于midmid的数字变成0,然后O(mlogn)O(mlog n)完成所有操作,最终如果在第qq为上的数字是1,那么最终答案一定大于等于midmid,否则小于midmid
    时间复杂度O(mlog2n)O(mlog^2n)


    代码:

    #include <cstdio>
    #include <string>
    #define reg register
    using namespace std;
    
    const int N=100010;
    int n,m,opt,l,r,mid,x,y,q,a[N],b[N];
    
    struct Ask
    {
    	int l,r,opt;
    }ask[N];
    
    struct Tree
    {
    	int l,r,lazy,sum;
    }tree[N*4];
    
    int read()
    {
    	int d=0;
    	char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch))
    		d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    void build(int x)
    {
    	tree[x].lazy=-1; tree[x].sum=0;
    	if (tree[x].l==tree[x].r)
    	{
    		tree[x].sum=a[tree[x].l];
    		return;
    	}
    	int mid=(tree[x].l+tree[x].r)>>1;
    	tree[x*2].l=tree[x].l;
    	tree[x*2].r=mid;
    	tree[x*2+1].l=mid+1;
    	tree[x*2+1].r=tree[x].r;
    	build(x*2); build(x*2+1);
    	tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
    }
    
    void pushdown(int x)
    {
    	if (tree[x].lazy!=-1)
    	{
    		tree[x*2].lazy=tree[x].lazy;
    		tree[x*2+1].lazy=tree[x].lazy;
    		tree[x*2].sum=tree[x].lazy*(tree[x*2].r-tree[x*2].l+1);
    		tree[x*2+1].sum=tree[x].lazy*(tree[x*2+1].r-tree[x*2+1].l+1);
    		tree[x].lazy=-1;
    	}
    }
    
    int find(int x,int l,int r)
    {
    	if (l>r) return 0;
    	if (tree[x].l==l && tree[x].r==r)
    		return tree[x].sum;
    	pushdown(x);
    	int mid=(tree[x].l+tree[x].r)>>1;
    	if (r<=mid) return find(x*2,l,r);
    	if (l>mid) return find(x*2+1,l,r);
    	return find(x*2,l,mid)+find(x*2+1,mid+1,r);
    }
    
    void change(int x,int l,int r,int val)
    {
    	if (l>r) return;
    	if (tree[x].l==l && tree[x].r==r)
    	{
    		tree[x].sum=val*(tree[x].r-tree[x].l+1);
    		tree[x].lazy=val;
    		return;
    	}
    	pushdown(x);
    	int mid=(tree[x].l+tree[x].r)>>1;
    	if (r<=mid) change(x*2,l,r,val);
    	else if (l>mid) change(x*2+1,l,r,val);
    	else change(x*2,l,mid,val),change(x*2+1,mid+1,r,val);
    	tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
    }
    
    int main()
    {
    	n=read(); m=read();
    	for (reg int i=1;i<=n;i++)
    		b[i]=read();
    	for (reg int i=1;i<=m;i++)
    		ask[i].opt=read(),ask[i].l=read(),ask[i].r=read();
    	q=read();
    	l=1; r=n;
    	while (l<=r)
    	{
    		mid=(l+r)>>1;
    		for (reg int i=1;i<=n;i++)
    			if (b[i]<mid) a[i]=0;
    				else a[i]=1;
    		tree[1].l=1; tree[1].r=n;
    		build(1);
    		for (reg int i=1;i<=m;i++)
    		{
    			int sum=find(1,ask[i].l,ask[i].r);
    			if (!ask[i].opt)
    			{
    				change(1,ask[i].l,ask[i].r-sum,0);
    				change(1,ask[i].r-sum+1,ask[i].r,1);
    			}
    			else
    			{
    				change(1,ask[i].l,ask[i].l+sum-1,1);
    				change(1,ask[i].l+sum,ask[i].r,0);
    			}
    		}
    		if (find(1,q,q)==1) l=mid+1;
    			else r=mid-1;
    	}
    	printf("%d
    ",l-1);
    	return 0;
    }
    
  • 相关阅读:
    2014.12.31今年最后的一天
    leetcode11 盛最多水的容器(Medium)
    leetcode23 合并k个排序链表(Hard)
    leetcode148 排序链表(Medium)
    leetcode48 旋转图像(Medium)
    leetcode227基本计算器II (Medium)
    leetcode338 比特位计数(Medium)
    leetcode32 最长有效括号(Hard)
    leetcode面试题10.01 合并排序的数组(Easy)
    leetcode55 跳跃游戏(Medium)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998063.html
Copyright © 2011-2022 走看看