JZOJ3854. 【NOIP2014八校联考第2场第2试9.28】分组(group) (Standard IO)

Description
Bsny所在的精灵社区有n个居民,每个居民有一定的地位和年龄,ri表示第i个人的地位,ai表示第i个人的年龄。
最近社区里要举行活动,要求几个人分成一个小组,小组中必须要有一个队长,要成为队长有这样的条件:
1、队长在小组中的地位应该是最高的(可以并列第一);
2、小组中其他成员的年龄和队长的年龄差距不能超过K。
有些人想和自己亲密的人组在同一个小组,同时希望所在的小组人越多越好。比如x和y想在同一个小组,同时希望它们所在的小组人越多越好,当然,它们也必须选一个符合上述要求的队长,那么问你,要同时包含x和y的小组,最多可以组多少人?
Input
第一行两个整数n和K;
接下来一行输入n个整数:r1, r2, …, rn
接下来一行输入n个整数:a1, a2, …, an
接下来输入Q表示有Q个询问;
接下来Q行每行输入x, y,表示询问:当x和y组在同一个小组,它们小组最多可以有多少人(x和y也有可能被选为队长,只要它们符合条件)。
Output
对于每个询问,输出相应的答案,每个答案占一行。
当x和y无法在同一组时,输出-1(比如x的年龄是1, y的年龄是100,K=1,无论谁当队长,x和y两者中,总会有人跟队长的年龄差距超过K,那么输出-1)。
Sample Input
5 1
1 5 4 1 2
4 4 3 2 2
4
5 3
2 3
2 5
4 1
Sample Output
4
3
-1
4
【样例解释】
询问1:当第5个人和第3个人想在一组时,小组成员可以有{1, 3, 4, 5},选择3当队长,而2不可以加入,因为2加入的话,5和2的年龄差距为2,超过K=1了;
询问2:当第2个人和第3个人想在一组时,可以选择{1, 2, 3};
询问3:当2和5想在一起时,无法满足要求;
询问4:当4和1想在一起时,可以选择{1, 3, 4, 5};
Data Constraint
20%的数据:2≤n≤100,0≤ k≤100,1≤ ri, ai ≤100,1≤ q≤ 100;
40%的数据:2≤ n≤1000,0≤ k≤ 1000,1≤ ri, ai ≤ 1000,1≤ q≤ 1000;
60%的数据:2≤ n≤ 10^4,0≤ k≤ 10^9,1≤ ri, ai ≤ 10^9, 1≤ q≤ 10^4;
100%的数据:2≤ n≤ 10^5,0≤ k≤ 10^9,1≤ ri, ai ≤ 10^9,1≤ q≤ 10^5,1≤ x, y≤ n, x≠y。

这道题想法挺妙的。
首先,对于每个队长,我们要算出他当队长时能够率领的人个数Cap[k](包括他自己)
自然先把“地位”与“年龄“”按地位从小到大排序
然后用权值线段树直接算即可(注意地位相同情况)
然后,如何算答案呢?
考虑将询问按照Max(x的地位,y的地位)排序,离线处理
因为为了将x,y放入同一个组中,队长地位必须大于等于Max(x的地位,y的地位)
把“地位”与“年龄“”按地位从大到小排序
将Cap依次扔入权值线段树中。
查询对应的年龄范围中最大的Cap即可。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int i,j,k,m,n,o,p,l,s,t,c,cnt,times,left,right,root;
struct node{
	int sta,age,id;
}a[200005],cc[200005];
struct wyd{
	int id,ans,x,y,px;
}b[200005];
struct WYD{
	int l,r,max,sum;
}tree[2500005];
int bz[200005],cap[200005];
int cmp1(node a,node b) {return a.sta<b.sta;}
int cmp2(node a,node b) {return a.id<b.id;}
int cmp3(wyd a,wyd b) {return a.px>b.px;}
int cmp4(wyd a,wyd b) {return a.id<b.id;}
void add_sum(int &x,int l,int r,int pos,int ad)
{
	if (!x) x=++cnt; 
	if (l==r) {
		tree[x].sum+=ad;return;
	}
	int mid=(l+r)>>1;
	if (pos<=mid) add_sum(tree[x].l,l,mid,pos,ad);
	else add_sum(tree[x].r,mid+1,r,pos,ad);
	tree[x].sum=tree[tree[x].l].sum+tree[tree[x].r].sum;
}
int query_sum(int x,int l,int r,int st,int en)
{
	if (l>=st&&r<=en) return tree[x].sum;
	int mid=(l+r)>>1;
	if (en<=mid) return query_sum(tree[x].l,l,mid,st,en);
	else if (st>mid) return query_sum(tree[x].r,mid+1,r,st,en);
	else return query_sum(tree[x].l,l,mid,st,mid)+query_sum(tree[x].r,mid+1,r,mid+1,en);
}
void add_max(int &x,int l,int r,int pos,int ad)
{
	if (!x) x=++cnt;
	if (l==r) {
		tree[x].max=max(tree[x].max,ad);return;
	}
	int mid=(l+r)>>1;
	if (pos<=mid) add_max(tree[x].l,l,mid,pos,ad);
	else add_max(tree[x].r,mid+1,r,pos,ad);
	tree[x].max=max(tree[tree[x].l].max,tree[tree[x].r].max);
}
int query_max(int x,int l,int r,int st,int en)
{
	if (l>=st&&r<=en) return tree[x].max;
	int mid=(l+r)>>1;
	if (en<=mid) return query_max(tree[x].l,l,mid,st,en);
	else if (st>mid) return query_max(tree[x].r,mid+1,r,st,en);
	else return max(query_max(tree[x].l,l,mid,st,mid),query_max(tree[x].r,mid+1,r,mid+1,en)); 
}
void read(int &x)
{
	x=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
}
int main()
{
	freopen("group.in","r",stdin);
	freopen("group.out","w",stdout);
	read(n),read(c);
	for (i=1;i<=n;i++) read(a[i].sta);
	for (i=1;i<=n;i++) read(a[i].age),a[i].id=i;
	sort(a+1,a+n+1,cmp1);cnt=1;
	memcpy(cc,a,sizeof(cc));root=1;
	for (i=1;i<=n;i++)
	{
		if (bz[i]) continue;
		bz[i]=1,add_sum(root,1,1e9,a[i].age,1);
		for (j=i+1;j<=n;j++)
			if (a[j].sta==a[j-1].sta) add_sum(root,1,1e9,a[j].age,1),bz[j]=1;
			else break;
		for (k=i;k<j;k++) cap[k]=query_sum(root,1,1e9,max(1,a[k].age-c),min(1000000000,a[k].age+c));
	}
	sort(a+1,a+n+1,cmp2);
	read(times);
	for (i=1;i<=times;i++) read(b[i].x),read(b[i].y),b[i].px=max(a[b[i].x].sta,a[b[i].y].sta),b[i].id=i;
	sort(b+1,b+times+1,cmp3);
	memset(tree,0,sizeof(tree));cnt=1;root=1;j=n;
	for (i=1;i<=times;i++)
	{
		if (a[b[i].x].age>a[b[i].y].age) swap(b[i].x,b[i].y);
		left=max(1,a[b[i].y].age-c),right=min(1000000000,a[b[i].x].age+c);
		for (;j>=1;j--)
		{
			if (cc[j].sta>=b[i].px) add_max(root,1,1e9,cc[j].age,cap[j]);
			else break;	
		} 
		if (left>right) b[i].ans=0;
		else b[i].ans=query_max(root,1,1e9,left,right);
	}
	sort(b+1,b+times+1,cmp4);
	for (i=1;i<=times;i++) 
	{
		if (!b[i].ans) b[i].ans--;
		printf("%d\n",b[i].ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值