BZOJ 4369: [IOI2015]teams分组 单调栈+主席树

这个题的思路还是十分巧妙的. 

我们发现我们要查询的区域恰好构成了一个梯形.  

然后用那个单调栈去维护折线,并用主席树做二维数点. 

code:

#include <cstdio> 
#include <algorithm> 
#include <stack>  
#include <cstring> 
#include <vector>  
#define N 500009    
#define lson s[x].ls
#define rson s[x].rs    
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;    
int n,tot,tmp;   
int rt[N],sor[N],id[N];  
struct data 
{
    int l,r,id;  
    data(int l=0,int r=0,int id=0):l(l),r(r),id(id){}      
}A[N<<1],up[N<<1];       
bool cmp(data a,data b) 
{
    return a.r==b.r?(a.id==b.id?a.l<b.l:a.id<b.id):a.r<b.r;                
}
bool cmp2(data a,data b) { return a.l<b.l; }  
namespace presist 
{
    struct node 
    {  
        int ls,rs,sum;     
    }s[N*70];    
    int newnode() { return ++tot; }   
    void build(int &x,int l,int r) 
    {
        x=newnode();  
        if(l==r) return;  
        int mid=(l+r)>>1;   
        build(lson,l,mid),build(rson,mid+1,r);    
    }
    int update(int x,int l,int r,int p,int v) 
    {
        int now=newnode();  
        s[now]=s[x],s[now].sum=s[x].sum+v;   
        if(l==r) return now;   
        int mid=(l+r)>>1;    
        if(p<=mid) s[now].ls=update(lson,l,mid,p,v);   
        else s[now].rs=update(rson,mid+1,r,p,v);    
        return now;     
    }
    int query(int x,int l,int r,int L,int R)
    {
        if(!x||L>R) return 0;   
        if(l>=L&&r<=R) return s[x].sum;  
        int mid=(l+r)>>1,re=0;   
        if(L<=mid)  re+=query(lson,l,mid,L,R);   
        if(R>mid)   re+=query(rson,mid+1,r,L,R);   
        return re;   
    }
    int get(int x,int y,int l,int r,int re) 
    {
        if(l==r) return l;    
        int mid=(l+r)>>1,p=s[s[x].ls].sum-s[s[y].ls].sum;  
        if(p>=re) return get(s[x].ls,s[y].ls,l,mid,re);  
        else return get(s[x].rs,s[y].rs,mid+1,r,re-p);   
    }
};         
struct sta
{    
    int h,r;   
    sta(int h=0,int r=0):h(h),r(r){}  
};   
stack<sta>S;   
void solve() 
{
    int i,j,m,sn=0;  
    scanf("%d",&m);  
    for(i=1;i<=m;++i) scanf("%d",&sor[i]),sn+=sor[i]; 
    if(sn>n) { printf("0\n"); return; }   
    sort(sor+1,sor+1+m);   
    while(!S.empty()) S.pop();  
    S.push(sta(tmp,0));         
    for(i=1;i<=m;++i) 
    {    
        int cur=id[sor[i]],remain=sor[i];  
        while(!S.empty()&&S.top().h<cur) S.pop();   
        while(remain&&!S.empty()) 
        {          
            int det=presist::query(rt[sor[i]],1,tmp,cur+1,S.top().h)-presist::query(rt[S.top().r],1,tmp,cur+1,S.top().h);
            if(det>=remain) 
            {     
                int c=presist::query(rt[sor[i]],1,tmp,1,cur)-presist::query(rt[S.top().r],1,tmp,1,cur);        
                cur=presist::get(rt[sor[i]],rt[S.top().r],1,tmp,remain+c);   
                remain=0;    
                break;   
            }             
            else 
            {
                remain-=det;  
                cur=S.top().h;     
                S.pop();    
            }
        }
        if(S.empty()) { printf("0\n"); return; }
        S.push(sta(cur,sor[i]));                          
    }
    printf("1\n");    
}
int main() 
{
    // setIO("input");                        
    int i,j,Q,cn=0;   
    scanf("%d",&n),tmp=n;   
    for(i=1;i<=n;++i) scanf("%d%d",&A[i].l,&A[i].r),A[i].id=1;  
    for(i=1;i<=n;++i) A[++tmp].l=0,A[tmp].r=i,A[tmp].id=-1;                   
    sort(A+1,A+1+tmp,cmp);   
    for(i=1;i<=tmp;++i) 
    {   
        if(A[i].id==-1)  id[A[i].r]=i; 
        else A[i].r=i,up[++cn]=A[i]; 
    }
    sort(up+1,up+1+n,cmp2),tmp+=2;                
    presist::build(rt[0],1,tmp);   
    for(j=i=1;i<=n;++i)   
    {
        rt[i]=rt[i-1];    
        while(j<=n&&up[j].l==i)                
            rt[i]=presist::update(rt[i],1,tmp,up[j].r,1),++j;    
    }            
    scanf("%d",&Q);  
    for(i=1;i<=Q;++i) solve();    
    return 0;
}    

  

上一篇:Office 365: 私有频道的使用和管理技巧


下一篇:云视频会议室的10大最佳实践(Microsoft Teams Room 篇)