CF369E Valera and Queries

一、题目

点此看题

二、解法

典型的正难则反,问题可以转化为有多少线段“夹在”给定的两个点之间(补齐最左边和最右边),那么拿总的线段数减去即是答案。

现在问题还是不好解决,因为判断包含需要两个量(左端点和右端点),如果消去一个量就会好很多。可以把问题离线下来,和给出的线段放在一起排序,左端点从大到小,相同的话给出线段在前,然后维护一个右端点的树状数组,就只用查右端点了。

#include 
#include 
using namespace std;
const int M = 300005;
#define up 1000000
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,b[M],ans[M],bit[up+5];
struct node
{
	int l,r,id,op;
	bool operator < (const node &b) const
	{
		if(l==b.l) return op<b.op;
		return l>b.l;
	}
}a[3*M];
int lowbit(int x)
{
	return x&(-x);
}
void add(int x)
{
	for(;x<=up;x+=lowbit(x))
		bit[x]++;
}
int sum(int x)
{
	int s=0;
	for(;x>=1;x-=lowbit(x))
		s+=bit[x];
	return s;
}
signed main()
{
	n=read();m=read();k=n;
	for(int i=1;i<=n;i++)
	{
		int l=read(),r=read();
		a[i]=node{l,r,i,0};
	}
	for(int w=1;w<=m;w++)
	{
		int t=read();b[0]=0;
		for(int i=1;i<=t;i++)
		{
			b[i]=read();
			if(b[i]>b[i-1]+1)
				a[++n]=node{b[i-1]+1,b[i]-1,w,1};
		}
		a[++n]=node{b[t]+1,up,w,1};
	}
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++)
    {
		if(a[i].op==0) add(a[i].r);
		else ans[a[i].id]+=sum(a[i].r);
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",k-ans[i]);
}

你可能感兴趣的:(线段树)