BZOJ 4369: [IOI2015]teams分组

把一个人看成二维平面上的一个点,把一个K[i]看成左上角为(0,+max),右下角为(K[i],K[i])的一个矩阵,那么可以很好地描述人对于询问是否合法(我也不知道他怎么想到这东西的)

然后把一组询问排序,按照K[i]从小到大依次处理,定义一个取点的方式为尽量取纵坐标小的点,那么可以构造出一种方案使得对于每一块区域不能取的点的最大纵坐标递减

(我也不知道他怎么想到这东西的)

单调栈维护每一块被取过的点的最大纵坐标,那么随着K[i]的增大,区域会被合并

二维平面数点,主席树

#include<cstdio>
#include<algorithm>
using namespace std;
int N,n,cnt,tree[10000005],sz[200005],h[200005],stack[200005],K[200005],root[500005],ls[10000005],rs[10000005];
struct node{
	int x,y;
}e[1000005];
bool cmp(node a,node b){
	return a.x<b.x;
}
void insert(int pre,int &now,int l,int r,int ID){
	now=++cnt;
	tree[now]=tree[pre]+1;
	ls[now]=ls[pre];
	rs[now]=rs[pre];
	if (l==r) return;
	int mid=(l+r)>>1;
	if (ID<=mid) insert(ls[pre],ls[now],l,mid,ID);
	else insert(rs[pre],rs[now],mid+1,r,ID);
}
int query_K(int pre,int now,int l,int r,int K){
	if (l==r) return l;
	int mid=(l+r)>>1;
	int ANS=tree[rs[now]]-tree[rs[pre]];
	if (ANS>=K) return query_K(rs[pre],rs[now],mid+1,r,K);
	else return query_K(ls[pre],ls[now],l,mid,K-ANS);
}
int query(int pre,int now,int l,int r,int K){
	if (!now) return 0;
	if (l==r) return tree[now]-tree[pre];
	int mid=(l+r)>>1;
	if (K<=mid) return tree[rs[now]]-tree[rs[pre]]+query(ls[pre],ls[now],l,mid,K);
	else return query(rs[pre],rs[now],mid+1,r,K);
}
int main(){
	scanf("%d",&n);
	for (int i=1; i<=n; i++) scanf("%d%d",&e[i].x,&e[i].y);
	sort(e+1,e+n+1,cmp);
	N=n+1;
	int head=1;
	for (int i=1; i<=N; i++){
		root[i]=root[i-1];
		while (head<=n && e[head].x==i) insert(root[i],root[i],1,N,e[head++].y);
	}
	int q;
	scanf("%d",&q);
	while (q--){
		int m;
		scanf("%d",&m);
		for (int i=1; i<=m; i++) scanf("%d",&K[i]);
		sort(K+1,K+m+1);
		int top=0;
		for (int i=1; i<=m; i++){
			while (top && h[top]<K[i]) top--;
			int Sz=sz[top];
			Sz+=query(root[K[stack[top]]],root[K[i]],1,N,K[i])-K[i];
			if (Sz<0){
				printf("0\n");
				break;
			}
			else if (i==m){
				printf("1\n");
				break;
			}
			int H=query_K(root[K[stack[top]]],root[K[i]],1,N,Sz-sz[top]);
			while (H>h[top] && top){
				top--;
				H=query_K(root[K[stack[top]]],root[K[i]],1,N,Sz-sz[top]);
			}
			stack[++top]=i;
			sz[top]=Sz;
			h[top]=H;
		}
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/silenty/p/9852419.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值