DUToj1222: Big brother said the calculation(二分查找 线段树)

1222: Big brother said the calculation

Time Limit:27000/25000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)
Total Submissions:44   Accepted:5

[Submit][Status][Discuss]

Description

(我们永远的)大哥有很多的小弟(n个)。每一个小弟有一个智力值。现在小弟们聚集在了大哥身旁,排成了一队,等待大哥的检阅。n个小弟的智力值是一个1到n的排列。大哥在检阅小弟时,每次会选择一些相邻的小弟,让他们按照自己的智力值从小到大或从大到小顺序重新排队(没有被选择的小弟位置不变),以便他排除其中的二五仔。在大哥检阅完小弟之后,老仙突然来了。他十分想为难一下大哥,所以他问大哥其中某一个小弟的智力值是多少。大哥十分的慌,并不能回答这个问题,所以让你来帮他解决这个问题。如果你能够解决,大哥可能会赠与你守护者的三叉戟和并教你他的换家绝学。

 

 

本题数据组数:3

 

Input

第一行3个整数n,q,k,表示小弟的数目,大哥检阅小弟时让一些小弟重新排队的次数,以及最后老仙问他的是第几个小弟的智力值。

第二行(1≤n≤100000)个整数,表示每个小弟的智力值,保证符合题意,是11到nn的一个排列。

接下来q(1≤q≤100000)行,每行3个整数a,b,t(1≤a≤b≤n,0≤t≤1)t(1≤a≤b≤n,0≤t≤1),表示他选择了第aa个到第bb个小弟(a,ba,b均包含)进行重新排列。若t=0,则为从小到大;若t=1,则为从大到小。

 

Output

输出一个整数,代表在检阅之后第k个小弟的智力值是多少。

 

Sample Input

5 2 4
1 4 3 2 5
1 3 0
3 5 1

Sample Output

4

HINT

 

 对样例的解释:

 

第一次操作之后,排列变为:1 3 4 2 5

 

第二次操作之后,排列变为:1 3 5 4 2

 

第四个数是4,所以输出4。

 

题意:给出一个有n个元素的数组(n<100000),然后给出q个区间(q<100000)和要求(从小到大排序还是从大到小排序)给这q个区间进行从小到大或从大到小排序。之后询问第k个元素的值。

解法:显然要使用二分查找。假设把大于等于ans的数设为1 小于的设为0,则排序后1的位置恰好就是原数组排序后大于等于ans的数的位置。那二分答案进行查找,当第k位恰好为1时就表示答案正确。那么排序怎么优化呢?因为全部是1,所以排序就询问区间和(假设为sum1)然后从大到小就把前面sum1个数设置为1其他设置0就好了,从小到大以此类推。

代码:

#include 
using namespace std;
#define N 100200
struct Node
{
    int r,l,sum;
    int lazy;
}tree[N<<3];
int ql[N],qr[N],qrank[N],qn;int a[N];int n;int q;int rrank;
void pushup(int pos)
{
    tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum;
}

void pushdown(int pos)
{
    if(tree[pos].lazy!=-1)
    {
        int mid=(tree[pos].l+tree[pos].r)/2;
        tree[pos*2].sum=tree[pos].lazy*(mid-tree[pos].l+1);
        tree[pos*2+1].sum=tree[pos].lazy*(tree[pos].r-mid);
        tree[pos*2].lazy=tree[pos*2+1].lazy=tree[pos].lazy;
        tree[pos].lazy=-1;
    }
}

void buildtree(int l,int r,int pos,int m)
{
    tree[pos].l=l;
    tree[pos].r=r;
    tree[pos].lazy=-1;
    if(l==r)
    {
    	if(a[l]>=m)
		tree[pos].sum=1;
		else{
			tree[pos].sum=0;
		}
        return;
    }
    int mid=(l+r)/2;
    buildtree(l,mid,pos*2,m);
    buildtree(mid+1,r,pos*2+1,m);
    pushup(pos);
}

void update(int l,int r,int c,int pos)
{
    if(tree[pos].l==l&&tree[pos].r==r)
    {
        tree[pos].sum=c*(r-l+1);
        tree[pos].lazy=c;
        return;
    }
    pushdown(pos);
    int mid=(tree[pos].l+tree[pos].r)/2;
    if(r<=mid)update(l,r,c,pos*2);
    else if(l>mid)update(l,r,c,pos*2+1);
    else{
    	update(l,mid,c,pos*2);update(mid+1,r,c,pos*2+1);
	}
    pushup(pos);
}

int query(int l,int r,int pos)
{
    if(tree[pos].l==l&&tree[pos].r==r)
    return tree[pos].sum;
    pushdown(pos);
    int mid=(tree[pos].l+tree[pos].r)/2;
    if(r<=mid)
        return query(l,r,pos*2);
    else if(l>mid)
        return query(l,r,pos*2+1);
    else return query(l,mid,pos*2)+query(mid+1,r,pos*2+1);
}

int check(int mid)//判断 
{
	    buildtree(1,n,1,mid); 
   	 for (int i = 1; i <=q; i++){
        int sum1 = query(ql[i], qr[i], 1);
        int sum0 = qr[i] - ql[i] + 1 - sum1;
        if (!qrank[i]){    
            if (sum0) update(ql[i], ql[i] + sum0 - 1, 0, 1);
            if (sum1) update(ql[i] + sum0, qr[i], 1, 1);
        }
        else{
            if (sum1) update(ql[i], ql[i] + sum1 - 1, 1, 1);
            if (sum0) update(ql[i] + sum1, qr[i], 0, 1);
        }
    }
    return query(rrank, rrank, 1);
}
int main()
{
    int mid;
     scanf("%d%d%d",&n,&q,&rrank);
     for(int i=1;i<=n;i++)
     {
     	scanf("%d",&a[i]);
	 }
     for(int i=1;i<=q;i++)
     {
     	scanf("%d%d%d",&ql[i],&qr[i],&qrank[i]);
	 }

    int l = 1, r = 100005;
    while (r - l > 1){
        mid = (l + r) >> 1;
        if (check(mid)) l = mid;
        else r = mid;
    }
    printf("%d\n", l);
    return 0;
}

 

你可能感兴趣的:(线段树,二分查找)