程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 樹套樹專題——bzoj 3110: [Zjoi2013] K大數查詢 & 3236 [Ahoi2013] 作業 題解

樹套樹專題——bzoj 3110: [Zjoi2013] K大數查詢 & 3236 [Ahoi2013] 作業 題解

編輯:C++入門知識

【原題1】

3110: [Zjoi2013]K大數查詢

Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 978 Solved: 476

Description

有N個位置,M個操作。操作有兩種,每次操作如果是1 a b c的形式表示在第a個位置到第b個位置,每個位置加入一個數c
如果是2 a b c形式,表示詢問從第a個位置到第b個位置,第C大的數是多少。

Input

第一行N,M
接下來M行,每行形如1 a b c或2 a b c

Output

輸出每個詢問的結果

Sample Input

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

Sample Output


1
2
1

HINT



N,M<=50000,N,M<=50000

a<=b<=N

1操作中abs(c)<=N

2操作中abs(c)<=Maxlongint

【傳送門】感謝這位大牛給我的啟發。http://www.cnblogs.com/lazycal/archive/2013/08/05/3239304.html

【分析】一直聽到過有一種神奇的數據結構——樹套樹。於是通過這道題我開始接觸這種算法。

樹套樹的本質就是兩棵樹套在一起(一般最外層的都是線段樹)。對於當前的這棵樹的每個結點可以再開一棵樹來維護。因為會爆內存,所以注意要動態開結點。說起來有點玄乎,先看看這道題吧。

我們可以先開一顆權值線段樹。對於當前結點K,它表示了權值范圍為a~b的所有結點的信息。但是有人要問:這樣怎麼控制位置范圍是l~r這個要求呢?我們可以在這個點上再開一棵vc+6w8DtveK1xE8ooclfockpT35+o6k8L3A+CjxwPjwvcD4KPHByZSBjbGFzcz0="brush:java;">#include #include using namespace std; const int N=50000+5; const int M=N*16*16; int root[N*4],n,m,sum[M],left[M],right[M],lazy[M],c,L,R,cnt,i,opt; inline int Read() { char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar()); int x=0;for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x; } void put(int &k,int l,int r) { if (!k) k=++cnt; if (L<=l&&r<=R) {lazy[k]++;sum[k]+=(r-l+1);return;} int mid=(l+r)/2; if (L<=mid) put(left[k],l,mid); if (R>mid) put(right[k],mid+1,r); sum[k]=sum[left[k]]+sum[right[k]]+lazy[k]*(r-l+1); } void update(int now,int l,int r) { put(root[now],1,n); if (l==r) return;int mid=(l+r)/2; if (c<=mid) update(now*2,l,mid); else update(now*2+1,mid+1,r); } int calc(int k,int l,int r) { if (!k) return 0; if (L<=l&&r<=R) return sum[k]; int mid=(l+r)/2,temp=0; if (L<=mid) temp+=calc(left[k],l,mid); if (R>mid) temp+=calc(right[k],mid+1,r); return temp+lazy[k]*(min(R,r)-max(L,l)+1); } int ask(int now,int l,int r) { if (l==r) return l; int mid=(l+r)/2,temp=calc(root[now*2],1,n); if (c<=temp) return ask(now*2,l,mid); c-=temp;return ask(now*2+1,mid+1,r); } int main() { n=Read();m=Read(); for (i=1;i<=m;i++) { opt=Read();L=Read();R=Read();c=Read(); if (opt==1) c=n-c+1,update(1,1,n); else printf("%d\n",n-ask(1,1,n)+1); } return 0; }

【原題】

3236: [Ahoi2013]作業

Time Limit: 100 Sec Memory Limit: 512 MB
Submit: 533 Solved: 225

Description

\

Input

\

Output

\

Sample Input

3 4
1 2 2
1 2 1 3
1 2 1 1
1 3 1 3
2 3 2 3

Sample Output

2 2
1 1
3 2
2 1

HINT


N=100000,M=1000000



【分析】這道題想熟練一下樹套樹,果斷自己碼代碼。第一問似乎比前面一題更加簡單,因為連lazy操作都不用,只要單點查詢,區間詢問即可。第二問真是費腦筋。想寫樹套樹也沒什麼事,可惜想法會復雜的多,像我這種初學者還是算了~~那怎麼辦呢?我又想到了莫隊算法!首先對於l,r,按照莫隊對它排序一下。因為要處理a~b的權值范圍,我們還要用樹狀數組來維護單點修改和區間詢問。

【代碼1】

#include
#include
#include
#define LL(x) (x&-x)
#define N 100005
#define M 17*17*N
#define Q 1000005
using namespace std;
int n,m,i,cnt,x,y,L,R,s,l,r,t1,t2,Num,ans;
int sum[M],left[M],right[M],root[N*3],data[N],pos[N],ans1[Q],ans2[Q],f[N],flag[N];
struct HHD{int l,r,id,x,y;}a[Q];
bool cmp(HHD a,HHD b)
{
  if (pos[a.l]!=pos[b.l]) return a.lb.r;
}
inline int Read()
{
  char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar());
  int x=0;for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
  return x;
}
void tree(int &k,int l,int r)
{
  if (!k) k=++cnt;sum[k]++;
  if (l==r) return;int mid=(l+r)>>1;
  if (i<=mid) tree(left[k],l,mid);
  else tree(right[k],mid+1,r);
}
void update(int k,int l,int r)
{
  while (l!=r)
  {
    tree(root[k],1,n);
    int mid=(l+r)>>1;
    if (data[i]<=mid) r=mid,k*=2;
    else l=mid+1,k=k*2+1;
  }
  tree(root[k],1,n);
}
int calc(int k,int l,int r)
{
  if (L<=l&&r<=R||!k) return sum[k];
  int mid=(l+r)>>1,o=0;
  if (L<=mid) o+=calc(left[k],l,mid);
  if (R>mid) o+=calc(right[k],mid+1,r);
  return o;
}
int ask(int k,int l,int r)
{
  if (x<=l&&r<=y) return calc(root[k],1,n);
  if (!root[k]) return 0;
  int mid=(l+r)>>1,o=0;
  if (x<=mid) o+=ask(k*2,l,mid);
  if (y>mid) o+=ask(k*2+1,mid+1,r);
  return o;
}
inline void add(int x,int c){for (;x<=n;x+=LL(x)) f[x]+=c;}
inline int Sum(int x){int o=0;for (;x;x-=LL(x)) o+=f[x];return o;}
int main()
{
  freopen("3236.in","r",stdin);
  freopen("3236.out","w",stdout);
  n=Read();m=Read();
  for (i=1;i<=n;i++)
    data[i]=Read(),update(1,1,n);
  s=int(sqrt(n));
  for (i=1;i<=n;i++) pos[i]=i/s+1;
  for (i=1;i<=m;i++)
  {
    L=Read(),R=Read(),x=Read(),y=Read();
    a[i].l=L;a[i].r=R;a[i].x=x;a[i].y=y;a[i].id=i;
    ans1[i]=ask(1,1,n);
  }
  sort(a+1,a+m+1,cmp);
  l=1;r=1;flag[data[1]]=1;add(data[1],1);
  for (i=1;i<=m;i++)
  {
    while (ra[i].l) {flag[data[--l]]++;if (flag[data[l]]==1) add(data[l],1);}
    while (r>a[i].r) {flag[data[r]]--;if (!flag[data[r]]) add(data[r],-1);r--;}
    while (l

【超時!】底下測70s,交上去就T了。哎!這麼辦呢?通過調試,我發現樹套樹M*LOG(N)^2的時間效率還是莫隊的M*LOG(N)*SQRT(N)的效率快!!於是忍痛割愛把樹套樹也改成了莫隊,然後底下40s,交上去60s過了。

【代碼2】

#include
#include
#include
#define LL(x) (x&-x)
#define N 100005
#define Q 1000005
using namespace std;
int n,m,i,cnt,x,y,L,R,s,l,r,t1,t2,Num,ans;
int data[N],pos[N],ans1[Q],ans2[Q],f[N],flag[N],g[N];
struct HHD{int l,r,id,x,y;}a[Q];
bool cmp(HHD a,HHD b)
{
  if (pos[a.l]!=pos[b.l]) return a.lb.r;
}
inline int Read()
{
  char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar());
  int x=0;for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
  return x;
}
inline void add(int x,int c){for (;x<=n;x+=LL(x)) f[x]+=c;}
inline int Sum(int x){int o=0;for (;x;x-=LL(x)) o+=f[x];return o;}
inline void add2(int x,int c){for (;x<=n;x+=LL(x)) g[x]+=c;}
inline int Sum2(int x){int o=0;for (;x;x-=LL(x)) o+=g[x];return o;}
int main()
{
  n=Read();m=Read();
  for (i=1;i<=n;i++)
    data[i]=Read();
  s=int(sqrt(n));
  for (i=1;i<=n;i++) pos[i]=i/s+1;
  for (i=1;i<=m;i++)
  {
    L=Read(),R=Read(),x=Read(),y=Read();
    a[i].l=L;a[i].r=R;a[i].x=x;a[i].y=y;a[i].id=i;
  }
  sort(a+1,a+m+1,cmp);
  l=1;r=1;flag[data[1]]=1;add(data[1],1);add2(data[1],1);
  for (i=1;i<=m;i++)
  {
    while (ra[i].l) {flag[data[--l]]++;if (flag[data[l]]==1) add(data[l],1);add2(data[l],1);}
    while (r>a[i].r) {flag[data[r]]--;if (!flag[data[r]]) add(data[r],-1);add2(data[r],-1);r--;}
    while (l

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved