poj 2481 Cows(树状数组||线段树)

poj 2481 题目链接

题意:给定n个区间,问对于每个区间,有多少个区间真包含该区间(真包含的意思是说,两个区间不能完全重合)

思路:

下面是一年前用树状数组过掉的时候写的题解:  和 star那道题差不多。

需要注意的是star那道题读入的时候已经排好了序,而这道题没有排序

由于答案是按照下表输出,排序的时候下标会被打乱,所以要存一下下表到结构体里。

另一个区别是,star那道题星星不会重复,就是说一个位置不会有多颗星星。

而这道题却可能,而且题目中说,如果区间的长度差为0(就是后面的那个不等式),那么不计数。

因为区间下标可能为0,但是树状数组的下表必须从1开始,所以下表要+1,但是由于下表可能有所重复,所以 不能用++,而是用+1

不然会增加多次(我就是这么w  a了两次。。。)

树状数组ac代码:

/*************************************************************************
	> File Name: code/poj/2481.cpp
	> Author: 111qqz
	> Email: rkz2013@126.com 
	> Created Time: 2015年08月04日 星期二 02时06分05秒
 ************************************************************************/
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
#define y0 abc111qqz
#define y1 hust111qqz
#define yn hez111qqz
#define j1 cute111qqz
#define tm crazy111qqz
#define lr dying111qqz
using namespace std;
#define REP(i, n) for (int i=0;i<int(n);++i)  
typedef long long LL;
typedef unsigned long long ULL;
const int inf = 0x7fffffff;
const int N=1E5+7;
int n;
int a[N],t[N];
struct Q
{
    int s,e,id;
}q[N];
bool cmp (Q a,Q b)
{
    if (a.e>b.e) return true;
    if (a.e==b.e&&a.s<b.s) return true;
    return false;
}
int lowbit(int x)
{
    return x&(-x);
}
void update ( int x,int c)
{
    for ( int i = x ; i <  N ; i = i + lowbit(i))
    {
	t[i] = t[i] + c;
    }
}
int Sum ( int x)
{
    int res = 0;
    for (  int i = x ; i >= 1 ; i = i - lowbit(i))
    {
	res = res + t[i];
    }
    return res;
}
int main()
{
    while (~scanf("%d",&n)&&n)
    {
	memset(t,0,sizeof(t));
	memset(a,0,sizeof(a));
	for ( int i = 0 ; i  < n; i ++ )
	{
	    scanf("%d %d",&q[i].s,&q[i].e);
	    q[i].id = i;
	}
	sort(q,q+n,cmp);
	a[q[0].id] = 0; //0是最强的...
	update(q[0].s+1,1);
	for ( int i = 1 ; i < n ;  i++)
	{
	    if (q[i].e==q[i-1].e&&q[i].s==q[i-1].s)
	    {
		a[q[i].id]=a[q[i-1].id];  // 完全一样,区间长度为0,不计数。
	    }
	    else
		a[q[i].id] = Sum (q[i].s+1);
	    update(q[i].s+1,1);
	}
	for ( int i = 0 ; i < n-1 ; i++ )
	    cout<<a[i]<<" ";
	    cout<<a[n-1]<<endl;
    }
  	return 0;
}

当然这道题也可以用线段树做。

先对点进行排序,右端点降序为第一关键字,左端点升序为第二关键字。(不唯一)

这样做有一个什么好处呢?

我们在处理第i个区间的时候,第i个区间前面的区间的右端点,一定是不小于第i个区间的右端点的,

此时我们只需要找到使左端点也满足题意的区间,也就是第i个区间前面的区间中,左端点不大于第i个区间的左端点的区间。

如果我们每次处理一个区间,都将该区间的左端点插入到线段树中(这句话可能比较抽象,线段树记录的是以某点为根节点的子树所代表的区间中点的个数,插入以后某一点记录为1,否则为0.),那么当我们询问第i个区间漆面的区间中左端点不大于第i个区间的左端点的区间的时候,其实也就是查询从1(这道题左端点的下标是从0开始,不过个人比较喜欢从1)到第i个区间的左端点之间有多少个点。

此外还需要注意两个区间重合的情况,这种情况是不能算在内的,要特殊处理。

线段树ac代码:

/* ***********************************************
Author :111qqz
Created Time :2016年09月04日 星期日 13时44分09秒
File Name :code/poj/r2481.cpp
************************************************ */
//之前这道题是用树状数组过的,这次写一个线段树版本
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define fst first
#define sec second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ms(a,x) memset(a,x,sizeof(a))
typedef long long LL;
#define pi pair < int ,int >
#define MP make_pair
using namespace std;
const double eps = 1E-8;
const int dx4[4]={1,0,0,-1};
const int dy4[4]={0,-1,1,0};
const int inf = 0x3f3f3f3f;
const int N=1E5+7;
int tree[N<<2];//tree的含义很简单,表示的就是以节点i为根的子树所代表的区间中的点的个数。(由于初始没有点插入,所以初始化tree为0.
int n ;
int ans[N];
struct node
{
    int l,r;
    int id; //由于排序后打乱了顺序,所以要记录id.
    bool operator < (node b)const
    {
	if (r!=b.r) return r>b.r;
	return l<b.l;
    }
    bool operator == (node b)const
    {
	return l==b.l&&r==b.r;
    }
}a[N];
void PushUp(int rt)
{
    tree[rt] = tree[rt<<1]+tree[rt<<1|1];
}
void update( int p,int l,int r,int rt)
{
    if (l==r)
    {
	tree[rt]++;
	return ;
    }
    int m = (l+r)>>1;
    if (p<=m) update(p,lson);
    else update(p,rson);
    PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    if (L<=l&&r<=R)
	return tree[rt];
    int m = (l+r)>>1;
    int ret = 0 ;
    if (L<=m)
    {
	int res = query(L,R,lson);
	ret = ret + res;
    }
    if (R>=m+1)
    {
	int res = query(L,R,rson);
	ret = ret + res;
    }
    return ret;
}
int main()
{
	#ifndef  ONLINE_JUDGE 
	freopen("code/in.txt","r",stdin);
  #endif
	while (~scanf("%d",&n))
	{
	    if (n==0) break;
	    for ( int i = 1 ; i <= n ; i++)
	    {
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].l++;
		a[i].r++; //index from 1 .
		a[i].id = i;
	    }
	    sort(a+1,a+n+1);
	    ms(ans,0);
	    ms(tree,0);
	    for ( int i = 1 ; i <= n ; i++)
	    {
		if (i>=2&&a[i]==a[i-1])
		    ans[a[i].id] = ans[a[i-1].id];
		else ans[a[i].id] = query(1,a[i].l,1,1E5,1);
		update(a[i].l,1,1E5,1);
	    }
	    for ( int i = 1 ; i <= n ; i++)
		printf("%d%c",ans[i],i==n?'\n':' ');
	}
  #ifndef ONLINE_JUDGE  
  fclose(stdin);
  #endif
    return 0;
}