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; }