Party(HDU-6521)


1 题意

  有 n n n个人,分别标号 1 , 2 , . . . , n 1,2,...,n 1,2,...,n,每次选择一个区间 [ l , r ] [l,r] [l,r],然后让这个区间里面的人举办一个 p a r t y party party,使得两两之间相互认识。问:每次举办 p a r t y party party之后产生了多少个新的关系。
  链接:link

2 思路

  令 a [ i ] = j ( j ⩽ i ) a[i]=j(j \leqslant i) a[i]=j(ji),表示 [ j , i ] [j,i] [j,i]区间内两两相互认识。
  假设在 [ l , r ] [l,r] [l,r]区间内举办了一个 p a r t y party party,对于所有的 a [ i ] a[i] a[i]

  1. 如果 a [ i ] ⩽ l a[i] \leqslant l a[i]l,则对于第 i i i个人来说,前面没有新认识的人。
  2. 如果 a [ i ] > l a[i] > l a[i]>l,则对于第 i i i个人来说,前面有 l − a [ i ] l-a[i] la[i]个新认识的人。

  基于上述思想,可以用线段树维护 a [ i ] a[i] a[i]的值,并维护区间最大值,这样对于第一种情况来说就不用更新到叶子节点。

  上述线段树是典型的势能线段树(大名鼎鼎的吉司机线段树),还可以继续做优化,这里暂不赘述。

2.1 时间复杂度分析

  在最坏情况下,区间选择是这样的 [ r − 1 , r ] , [ r − 2 , r ] , . . . , [ 1 , r ] [r-1,r],[r-2,r],...,[1,r] [r1,r],[r2,r],...,[1,r],使得每次都要更新叶子节点,时间复杂度为 O ( ∑ i = 1 n ( i × l o g ( i ) ) ) \mathcal{O}( \sum_{i=1}^{n}(i \times log(i))) O(i=1n(i×log(i))),接近 O ( n 2 log ⁡ ( n ) ) \mathcal{O}(n^{2} \log(n)) O(n2log(n))

2.2 实现

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5e5+5;
struct Node{
    int val,mxv;
}node[N<<2];
int a[N];
int builtTree(int ind,int be,int en){
    if(be==en) return node[ind].val=node[ind].mxv=a[be];
    int mid=(be+en)>>1;
    return node[ind].mxv=max(builtTree(ind<<1,be,mid),builtTree((ind<<1)+1,mid+1,en));
}
int query(int ind,int be,int en,int fbe,int fen,int val){
    if(val>=node[ind].mxv) return 0;
    int cnt=0;
    if(be==en){
        cnt+=node[ind].val-val;
        node[ind].val=node[ind].mxv=val;
        return cnt;
    }
    int mid=(be+en)>>1;
    if(mid>=fen) cnt=query(ind<<1,be,mid,fbe,fen,val);
    else if(mid<fbe) cnt=query((ind<<1)+1,mid+1,en,fbe,fen,val);
    else cnt=query(ind<<1,be,mid,fbe,mid,val)+query((ind<<1)+1,mid+1,en,mid+1,fen,val);
    node[ind].mxv=max(node[ind<<1].mxv,node[(ind<<1)+1].mxv);
    return cnt;
}
int main(){
    int n,m;
    while(~scanf("%d %d",&n,&m)){
        for(int i=1;i<=n;i++) a[i]=i;
        builtTree(1,1,n);
        while(m--){
            int x,y;scanf("%d %d",&x,&y);
            printf("%d\n",query(1,1,n,x,y,x));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值