题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6521
题意:给定n,m表示有编号1~n的人,m次询问,每次询问给出l,r问这个区间内的人有多少两两组合在之前没有出现过,对于每次询问输出符合的组数。
例如[1,4] 可以有 1-2 1-3 1-4 2-3 2-4 3-4 6种
再给你[2,5] 则只有 2-5 3-5 4-5 3种(其余的上面已经出现过)
数据范围:n≤5e5,m≤5e5
思路:吉司机线段树,在线段树上维护一个最大值,这个最大值代表对于线段树这个节点管辖的区间内每个点之前被输出的最靠左的位置比如上面的[1,4],那么这个区间内的每一个点的ma就是1,向上更新时要把区间内最大的那个左位置传上去。访问时如果对于线段树上的这个节点上它所有管辖的点的最左的最大值都小于我要查询的区间的l,那么这个线段树的节点的贡献就是0,如果这个节点上管辖的点的最左有落在查询的这个区间[l,r]内那么只能一直更新到子节点,计算贡献就是tree[x].ma-l,然后全部求和就是答案。
#include <bits/stdc++.h>
#define ls (x<<1)
#define rs (x<<1|1)
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,m,t;
struct Tree{
int l,r,ma;
}tree[N<<2];
void build(int x,int l,int r){
tree[x].l=l,tree[x].r=r,tree[x].ma=0;
if(l==r)tree[x].ma=l;
else{
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
tree[x].ma=max(tree[ls].ma,tree[rs].ma);
}
}
ll update(int x,int l,int r,int pos){
ll res=0;int L=tree[x].l,R=tree[x].r;
int mid=(L+R)>>1;
if(tree[x].ma<=pos)return res;
if(L==R){
res=tree[x].ma-pos;
tree[x].ma=pos;
return res;
}
if(l<=mid)res+=update(ls,l,r,pos);
if(r>mid)res+=update(rs,l,r,pos);
tree[x].ma=max(tree[ls].ma,tree[rs].ma);
return res;
}
int main(){
while(~scanf("%d%d",&n,&m)){
build(1,1,n);
int l,r;
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&r);
printf("%lld\n",update(1,l,r,l));
}
}
return 0;
}