VIJOS P1081 野生动物园 SBT、划分树模板
【描述】
cjBBteam拥有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从南到北被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从北到南编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。饲养员cmdButtons决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强的狮子进行投喂。值得注意的是,cmdButtons不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此cmdButtons的投喂区间是互不包含的。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。
【输入格式】
输入第一行有两个数N和M。此后一行有N个数,从南到北描述狮子的觅食能力值。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,cmdButtons选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。
【输出格式】
输出有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。
【样例输入】
7 2 1 5 2 6 3 7 4 1 5 3 2 7 1
【样例输出】
3 2
【分析】
平衡树解法:
由题目给出的区间互相不包含可以得出,若将每次询问的区间按照起始区域进行排序,那一定是一段接一段,只有可能是两种情况:
下一段的左端与上一段的右端不相交或者相交。
这两种情况都是前面的数据与后面的数据互不影响,因此将区间排序之后,对于每一个区间,删除掉前面多余的,插入后面不够的,使平衡树中仅留下该区间中的数据,然后直接找第k小即可。
SBT可解。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : VIJOS1081 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 12 using namespace std; 13 14 #define MAXN 100010 15 16 int sons[MAXN][2]; 17 int size[MAXN],data[MAXN]; 18 int sbt=0,sbttail=0; 19 20 void rotate(int &t,int w) //rotate(&node,0/1) 21 { 22 int k=sons[t][1-w]; 23 if (!k) return ; 24 sons[t][1-w]=sons[k][w]; 25 sons[k][w]=t; 26 size[k]=size[t]; 27 size[t]=size[sons[t][0]]+size[sons[t][1]]+1; 28 t=k; 29 } 30 31 void maintain(int& t,bool flag) //maintain(&node,flag) 32 { 33 if (!t) return ; 34 if (!flag) 35 if (size[sons[sons[t][0]][0]]>size[sons[t][1]]) rotate(t,1); 36 else if (size[sons[sons[t][0]][1]]>size[sons[t][1]]) 37 { 38 rotate(sons[t][0],0); 39 rotate(t,1); 40 } else return ; 41 else 42 if (size[sons[sons[t][1]][1]]>size[sons[t][0]]) rotate(t,0); 43 else if (size[sons[sons[t][1]][0]]>size[sons[t][0]]) 44 { 45 rotate(sons[t][1],1); 46 rotate(t,0); 47 } else return ; 48 49 maintain(sons[t][0],false); 50 maintain(sons[t][1],true); 51 maintain(t,false); 52 maintain(t,true); 53 } 54 55 void insert(int& t,int v) //insert(&root,0,value) 56 { 57 if (!t) 58 { 59 sbttail++; 60 data[sbttail]=v; 61 size[sbttail]=1; 62 sons[sbttail][0]=0; 63 sons[sbttail][1]=0; 64 t=sbttail; 65 } else 66 { 67 size[t]++; 68 if (v<data[t]) insert(sons[t][0],v); 69 else insert(sons[t][1],v); 70 maintain(t,v>=data[t]); 71 } 72 } 73 74 int del(int& t,int v) //del(&root,key) 75 { 76 size[t]--; 77 if (v==data[t]||(v<data[t]&&sons[t][0]==0)||(v>data[t]&&sons[t][1]==0)) 78 { 79 int ret=data[t]; 80 if (sons[t][0]==0||sons[t][1]==0) t=sons[t][1]+sons[t][0]; 81 else data[t]=del(sons[t][0],data[t]+1); 82 return ret; 83 } else 84 if (v<data[t]) return del(sons[t][0],v); 85 else return del(sons[t][1],v); 86 } 87 88 int select(int t,int k) 89 { 90 if (k==size[sons[t][0]]+1) return t; 91 if (k<=size[sons[t][0]]) return select(sons[t][0],k); 92 else return select(sons[t][1],k-1-size[sons[t][0]]); 93 } 94 95 typedef struct nod 96 { 97 int i,l,r,k; 98 } node; 99 node d[50010]; 100 101 bool op(node a,node b) 102 { 103 if (a.l==b.l) return a.r<b.r; 104 else return a.l<b.l; 105 } 106 107 int a[MAXN]; 108 109 typedef struct nod1 110 { 111 int i,ans; 112 } node1; 113 node1 out[50010]; 114 115 bool op1(node1 a,node1 b) 116 { 117 return a.i<b.i; 118 } 119 120 int main() 121 { 122 freopen("1.txt","r",stdin); 123 124 sbt=0,sbttail=0; 125 int n,m; 126 scanf("%d%d",&n,&m); 127 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 128 for (int i=1;i<=m;i++) 129 { 130 scanf("%d%d%d",&d[i].l,&d[i].r,&d[i].k); 131 d[i].i=i; 132 } 133 sort(&d[1],&d[m+1],op); 134 135 int l=0,r=0; 136 for (int i=1;i<=m;i++) 137 { 138 if (r<d[i].l) 139 { 140 sbt=0; 141 sbttail=0; 142 for (int j=d[i].l;j<=d[i].r;j++) insert(sbt,a[j]); 143 } else 144 { 145 for (int j=l;j<d[i].l;j++) del(sbt,a[j]); 146 for (int j=r+1;j<=d[i].r;j++) insert(sbt,a[j]); 147 } 148 l=d[i].l; 149 r=d[i].r; 150 int temp=select(sbt,d[i].k); 151 152 out[i].i=d[i].i; 153 out[i].ans=data[temp]; 154 } 155 156 sort(&out[1],&out[m+1],op1); 157 for (int i=1;i<=m;i++) printf("%d\n",out[i].ans); 158 159 return 0; 160 }
划分树解法:
划分树是一种类似快排的数据结构,可以快速在O(logn)的时间内直接求出某个区间内的k值。
然后本题就是......一棵裸的划分树,直接套即可
。。。。。。最后的结果是,不知道为什么比SBT要慢很多,直观的感觉上划分树没有多余的删除操作,应该会快很多的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : VIJOS1081_SortTree 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 12 using namespace std; 13 14 #define MAXN 100010 15 16 int a[MAXN],dp[20][MAXN],tree[20][MAXN]; 17 18 void maketree(int c,int l,int r) 19 { 20 int mid=(l+r)/2,ls=l,rs=mid+1,num=0; 21 22 for (int i=mid;i>=l&&a[i]==a[mid];i--) num++; 23 for (int i=l;i<=r;i++) 24 { 25 if (i==l) dp[c][i]=0; 26 else dp[c][i]=dp[c][i-1]; 27 28 if (tree[c][i]<a[mid]) 29 { 30 dp[c][i]++; 31 tree[c+1][ls]=tree[c][i]; 32 ls++; 33 } else 34 if (tree[c][i]>a[mid]) 35 { 36 tree[c+1][rs]=tree[c][i]; 37 rs++; 38 } else 39 { 40 if (num) 41 { 42 num--; 43 dp[c][i]++; 44 tree[c+1][ls]=tree[c][i]; 45 ls++; 46 } else 47 { 48 tree[c+1][rs]=tree[c][i]; 49 rs++; 50 } 51 } 52 } 53 54 if (l==r) return ; 55 maketree(c+1,l,mid); 56 maketree(c+1,mid+1,r); 57 } 58 59 int query(int c,int l,int r,int ql,int qr,int k) 60 { 61 if (l==r) return tree[c][l]; 62 int s,ss,mid=(l+r)/2; 63 64 if (l==ql) 65 { 66 s=0; 67 ss=dp[c][qr]; 68 } else 69 { 70 s=dp[c][ql-1]; 71 ss=dp[c][qr]-s; 72 } 73 if (k<=ss) return query(c+1,l,mid,l+s,l+s+ss-1,k); 74 else return query(c+1,mid+1,r,mid-l+1+ql-s,mid-l+1+qr-s-ss,k-ss); 75 } 76 77 int main() 78 { 79 //freopen("1.in","r",stdin); 80 //freopen("zoo8.in","r",stdin); 81 //freopen("1.out","w",stdout); 82 83 int n,m; 84 scanf("%d%d",&n,&m); 85 86 for (int i=1;i<=n;i++) 87 { 88 scanf("%d",&a[i]); 89 tree[0][i]=a[i]; 90 } 91 sort(&a[1],&a[n+1]); 92 93 maketree(0,1,n); 94 95 for (int i=1;i<=m;i++) 96 { 97 int l,r,k; 98 scanf("%d%d%d",&l,&r,&k); 99 printf("%d\n",query(0,1,n,l,r,k)); 100 } 101 102 return 0; 103 }
Do Cool Things That Matter!