题目链接
题意:
有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);
}
}
}