HDU 6521 Party(思维+STL/吉司机线段树)

题目链接

题意:

有n个人,m场派对,n个人一开始互相不认识。

每一场派对,你需要输出有多少对人,是第一次互相见面

解析:

这道题大佬的思路

维护a[i],表示[1..i]之内i最远认识到谁,即[a[i]...i)的人,i都已经认识了。

 

那么对于询问[l,r],我们需要更新i∈[l,r] a[i]=min(a[i],l)

同时计算贡献是ans+=a[i]-l

算这个有两种做法1.思维乱搞(依赖a的递增性)  2.吉司机线段树

然后上面这样复杂度是O(n*n),我们需要优化一下

然后很关键的一点是,因为询问的区间是连续的,所以a[]一定是递增的。

之后我们就要把a[]当作链表一样来操作了。

pre[i],表示在i点之前的下标是多少

对于[l,r],从第一个下标>r的点k往前遍历

如果a[k]>l,并且如果k点包括[l,r]内的点(r>pre[k]),那么就要计算k点的贡献

然后往前遍历,途中如果a[i]>=l,那么都计算贡献。然后把这个点删除(因为这些点会并入之后我们插入的点),

直到遇到一个点a[j]

上面不理解的可以举个样例按照上面说的步骤走一下

代码还是很简单的,但是很多细节

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
const int N = 5e5+10;
int pre[N],a[N];
set lian;

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        lian.clear();
        for(int i=1;i<=n+1;i++)
            a[i]=i,pre[i]=i-1,lian.insert(i);
        set::iterator it;
        ll ans=0;
        for(int i=1;i<=m;i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            it=lian.upper_bound(r);
            int fi=*it;
            ans=0;

            if(a[fi]>=l) 
            {
                ans+=1ll*(r-pre[fi])*(a[fi]-l);
                int u=pre[fi];
                
                    while(u&&a[u]>=l)
                    {
                        ans+=1ll*(a[u]-l)*(u-pre[u]);
                        lian.erase(u);
                        u=pre[u];
                    }
                    pre[fi]=r;
                    if(lian.count(r)==0)
                    {
                        a[r]=l;
                        pre[r]=u;
                        lian.insert(r);
                    }
                    else
                    {
                        if(a[r]>l) a[r]=l;
                        if(u!=r) pre[r]=u;
                    }
                    
                
            }
            printf("%lld\n",ans);
        }
    }
    
}

用吉司机线段树,每一次询问,先求一遍[l,r]的和,然后更新之后,再求一遍和,

两个和之差就是答案了,这种做法就不依赖a[]是不是递增的性质了,就是时间常数有点大

#include 
#include 
using namespace std;
const int N=1000050;
int cases,n,m,op,xx,yy,zz;
typedef long long ll;
struct SegTree{int max1,max2,maxnum,lazy;ll sum;}tr[N*8];
void push_up(int pos){
    tr[pos].max2=0;int lson=pos<<1,rson=pos<<1|1;
    tr[pos].max1=max(tr[lson].max1,tr[rson].max1);
    if(tr[lson].max1==tr[rson].max1)
        tr[pos].maxnum=tr[lson].maxnum+tr[rson].maxnum,
        tr[pos].max1=tr[lson].max1,
        tr[pos].max2=max(tr[lson].max2,tr[rson].max2);
    else{
        tr[lson].max1>tr[rson].max1?tr[pos]=tr[lson]:tr[pos]=tr[rson];
        tr[pos].max2=max(tr[lson].max2,tr[rson].max2);
        if(tr[pos].max1==tr[lson].max1)tr[pos].max2=max(tr[pos].max2,tr[rson].max1);
        else tr[pos].max2=max(tr[pos].max2,tr[lson].max1);
    }tr[pos].sum=tr[lson].sum+tr[rson].sum;
}
void change(int pos,int wei){
    tr[pos].sum-=(1ll*tr[pos].max1-wei)*tr[pos].maxnum;
    tr[pos].max1=tr[pos].lazy=wei;
}
void push_down(int pos){
    int lson=pos<<1,rson=pos<<1|1;
    if(tr[lson].max1>tr[pos].lazy)change(lson,tr[pos].lazy),tr[lson].lazy=tr[pos].lazy;
    if(tr[rson].max1>tr[pos].lazy)change(rson,tr[pos].lazy),tr[rson].lazy=tr[pos].lazy;
    tr[pos].lazy=-1;
}
void build(int l,int r,int pos){
    tr[pos].lazy=-1,tr[pos].max2=0;
	if(l==r){tr[pos].max1=l,tr[pos].sum=tr[pos].max1,tr[pos].maxnum=1;return;}
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    build(l,mid,lson),build(mid+1,r,rson),push_up(pos);
}
void insert(int l,int r,int pos,int L,int R,int wei){
    if(~tr[pos].lazy)push_down(pos);
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(l>=L&&r<=R){
        if(wei>=tr[pos].max1)return;
        else if(weitr[pos].max2){change(pos,wei);return;}
        else insert(l,mid,lson,L,R,wei),insert(mid+1,r,rson,L,R,wei);
    }
    if(mid=R)insert(l,mid,lson,L,R,wei);
    else insert(l,mid,lson,L,R,wei),insert(mid+1,r,rson,L,R,wei);
    push_up(pos);
}
ll query_sum(int l,int r,int pos,int L,int R){
    if(~tr[pos].lazy)push_down(pos);
    if(l>=L&&r<=R)return tr[pos].sum;
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(mid=R)return query_sum(l,mid,lson,L,R);
    else return query_sum(l,mid,lson,L,R)+query_sum(mid+1,r,rson,L,R);
}
int query_max(int l,int r,int pos,int L,int R){
    if(~tr[pos].lazy)push_down(pos);
    if(l>=L&&r<=R)return tr[pos].max1;
    int mid=(l+r)>>1,lson=pos<<1,rson=pos<<1|1;
    if(mid=R)return query_max(l,mid,lson,L,R);
    else return max(query_max(l,mid,lson,L,R),query_max(mid+1,r,rson,L,R));
}
int read(){
    char p=getchar();int x=0;
    while(p<'0'||p>'9')p=getchar();
    while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
    return x;
}
signed main(){
    
    while(scanf("%d%d",&n,&m)!=EOF){
        build(1,n,1);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&xx,&yy);
			ll ans=query_sum(1,n,1,xx,yy);
            insert(1,n,1,xx,yy,xx);
            ll res=query_sum(1,n,1,xx,yy);
			printf("%lld\n",ans-res);
        }
    }
}

 

你可能感兴趣的:(思维题,STL容器,线段树)