1. 程式人生 > >【BZOJ3236】【AHOI2013】作業 線段樹 分治 樹狀陣列

【BZOJ3236】【AHOI2013】作業 線段樹 分治 樹狀陣列

題目描述

  給你一個長度為n的數列,還有m個詢問,對於每個詢問(l,r,a,b),輸出1.區間[l,r]有多少範圍在[a,b]的數;2.區間[l,r]有多少範圍在[a,b]的權值。

  n100000,m1000000

題解

  這道題莫隊可以水過。

  這裡講一個更優秀的演算法。

  建一棵權值線段樹。每一個點存它代表的範圍內所有數的下標。

  一個詢問對應權值線段樹中的一些點。每個點要求出[l,r]內的數的個數和不同的數的個數。第一問可以亂搞。第二問直接排序後用樹狀陣列維護每個數最後一次出現的位置然後離線亂搞。

  每個點會在根到這個點的路徑上各插入一次,所以總的點數是nlogn

  總的時間複雜度是O

((n+m)log2n)

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int
,int> pii; typedef pair<ll,ll> pll; void sort(int &a,int &b) { if(a>b) swap(a,b); } void open(const char *s) { #ifndef ONLINE_JUDGE char str[100]; sprintf(str,"%s.in",s); freopen(str,"r",stdin); sprintf(str,"%s.out",s); freopen(str,"w",stdout); #endif
} struct ques { int l,r,a,b,id; }; vector<ques> q[200010]; vector<pii> c[200010]; struct node { int l,r,ls,rs; }; node a[200010]; int cnt; int rt; void build(int &p,int l,int r) { p=++cnt; a[p].l=l; a[p].r=r; if(l==r) return; int mid=(l+r)>>1; build(a[p].ls,l,mid); build(a[p].rs,mid+1,r); } void build2(int p) { if(a[p].l==a[p].r) return; int mid=(a[p].l+a[p].r)>>1; for(vector<pii>::iterator x=c[p].begin();x!=c[p].end();x++) if(x->second<=mid) c[a[p].ls].push_back(*x); else c[a[p].rs].push_back(*x); build2(a[p].ls); build2(a[p].rs); } void add(int &p,ques &v) { if(v.a<=a[p].l&&v.b>=a[p].r) { q[p].push_back(v); return; } int mid=(a[p].l+a[p].r)>>1; if(v.a<=mid) add(a[p].ls,v); if(v.b>mid) add(a[p].rs,v); } int ans[1000010]; int ans2[1000010]; int d[100010]; int e[100010]; int f[100010]; int n,m; int bt[100010]; int bt2[100010]; int last[100010]; void add(int x,int v) { for(;x<=n;x+=x&-x) bt[x]+=v; } int sum(int x) { int s=0; for(;x;x-=x&-x) s+=bt[x]; return s; } void add2(int x,int v) { for(;x<=n;x+=x&-x) bt2[x]+=v; } int sum2(int x) { int s=0; for(;x;x-=x&-x) s+=bt2[x]; return s; } void rd(int &s) { int c; while((c=getchar())<'0'||c>'9'); s=c-'0'; while((c=getchar())>='0'&&c<='9') s=s*10+c-'0'; } int cmp(ques a,ques b) { return a.r<b.r; } int main() { open("bzoj3236"); scanf("%d%d",&n,&m); int i,x; build(rt,1,n); for(i=1;i<=n;i++) { rd(x); c[1].push_back(pii(i,x)); } build2(rt); ques v; for(i=1;i<=m;i++) { rd(v.l); rd(v.r); rd(v.a); rd(v.b); v.id=i; add(rt,v); } int j; for(i=1;i<=cnt;i++) { int sz2=q[i].size(); if(!sz2) continue; int sz=c[i].size(); int k=1; for(j=1;j<=sz;j++) { d[j]=c[i][j-1].first; f[j]=c[i][j-1].second; } sort(q[i].begin(),q[i].end(),cmp); while(k<=sz2&&q[i][k-1].r<d[1]) k++; for(j=1;j<=sz;j++) { e[j]=last[f[j]]; last[f[j]]=j; if(e[j]) add(e[j],-1); add(j,1); add2(j,1); while(k<=sz2&&(j==sz||q[i][k-1].r<d[j+1])) { int l=lower_bound(d+1,d+sz+1,q[i][k-1].l)-d; ans[q[i][k-1].id]+=sum(j)-sum(l-1); ans2[q[i][k-1].id]+=sum2(j)-sum2(l-1); k++; } } for(j=1;j<=sz;j++) { if(last[f[j]]) add(last[f[j]],-1); last[f[j]]=0; add2(j,-1); } } for(i=1;i<=m;i++) printf("%d %d\n",ans2[i],ans[i]); return 0; }