1 /************************************************************* 2 题目: Feed the dogs(poj 2761) 3 链接: http://poj.org/problem?id=2761 4 题意: 给一个数列,在给一些区间,求这些区间第k小的值,这些 5 区间没有包含关系,也就是说如果La>Lb那么Ra>Rb; 6 算法: treap树 7 思路: 由于这些区间没有包含关系,把这些区间排序后从左边 8 开始计算,每计算一个区间把前面多余数删除,这样每 9 个点只要插入和删除一次就可以了。 10 **************************************************************/ 11 #include<cstdio> 12 #include<cstring> 13 #include<algorithm> 14 #include<iostream> 15 using namespace std; 16 17 const int mx=1000005; 18 struct S 19 { 20 int l, r; 21 int di,k; 22 }; 23 S s[mx]; 24 int a[mx]; 25 int ans[mx]; 26 int n,m; 27 bool com(S a,S b) 28 { 29 if (a.l==b.l) return a.r<b.r; 30 return a.l<b.l; 31 } 32 33 typedef struct Node ///节点的结构体 34 { 35 Node *l,*r; 36 int val,pri; ///节点的值和优先级 37 int sz; ///节点子树的节点数 38 Node(int x) ///初始化节点 39 { 40 l=r=NULL; 41 val=x; 42 pri=rand(); 43 sz=1; 44 } 45 }Node; 46 Node *root; 47 48 int Tsize(Node *T) ///计算子树的叶子节点 49 { 50 if (T==NULL) return 0; 51 return T->sz; 52 } 53 Node *L_rotate(Node *T) ///右节点的优先级大于当前节点的优先级,进行左旋转 54 { 55 Node *A=T->r; ///A表示有节点 56 T->r=A->l; 57 A->l=T; 58 A->sz-T->sz; ///交换后A变成当前节点,所以它的子树的节点数等于原来节点的节点数 59 T->sz=Tsize(T->l)+Tsize(T->r)+1; 60 return A; 61 } 62 Node *R_rotate(Node *T) ///左节点的优先级大于当前节点的优先级,进行右旋转 63 { 64 Node *A=T->l; 65 T->l=A->r; 66 A->r=T; 67 A->sz=T->sz; 68 T->sz=Tsize(T->l)+Tsize(T->r)+1; 69 return A; 70 } 71 72 void inser(Node *&T,int val) ///插入函数,和二叉排序树差不多 73 { 74 if (T==NULL) 75 { 76 T=new Node(val); 77 return ; 78 } 79 if (T->val>=val) 80 { 81 inser(T->l,val); 82 if ((T->l->pri)<(T->pri)) T=R_rotate(T); ///优先级比较,并旋转 83 } 84 else 85 { 86 inser(T->r,val); 87 if ((T->r->pri)<(T->pri)) T=L_rotate(T); 88 } 89 T->sz=Tsize(T->l)+Tsize(T->r)+1; 90 } 91 92 void Delete(Node *&T,int val) ///删除函数 93 { 94 if (T->val>val) Delete(T->l,val); 95 else if (T->val<val) Delete(T->r,val); 96 else 97 { 98 if (T->l==NULL&&T->r==NULL) T=NULL; ///左右节点都为空 99 else if (T->r==NULL) T=T->l; ///右节点为空 100 else if(T->l==NULL) T=T->r; ///左节点为空 101 else ///左右都不空 102 { 103 if (T->l->pri<T->r->pri) ///左节点优先级小于右边 104 { ///右旋转,并向右子树删除 105 T=R_rotate(T); ///应为有旋转后,要删除的节点到有子树去了 106 Delete(T->r,val); 107 } 108 else 109 { 110 T=L_rotate(T); 111 Delete(T->l,val); 112 } 113 } 114 } 115 if (T!=NULL) 116 { 117 T->sz=Tsize(T->l)+Tsize(T->r)+1; 118 } 119 } 120 121 int Find(Node *T,int k) ///查找第k小的树 122 { 123 int temp=Tsize(T->l)+1; ///temp小于等于T->val数的个数 124 if (temp==k) return T->val; 125 if (temp>k) return Find(T->l,k); 126 return Find(T->r,k-temp); 127 } 128 129 130 void solve() 131 { 132 sort(s+1,s+m+1,com); 133 s[0].l=-1; 134 s[0].r=-2; 135 for (int i=1;i<=m;i++) 136 { 137 for (int j=s[i-1].l;j<=min(s[i-1].r,s[i].l-1);j++) 138 Delete(root,a[j]); ///删除比要计算区间多的数 139 for (int j=max(s[i-1].r+1,s[i].l);j<=s[i].r;j++) 140 inser(root,a[j]); ///插入该区间剩下的数 141 ans[s[i].di]=Find(root,s[i].k); 142 } 143 for (int j=s[m].l;j<=s[m].r;j++) Delete(root,a[j]); 144 } 145 146 int main() 147 { 148 scanf("%d%d",&n,&m); 149 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 150 for (int i=1;i<=m;i++) 151 { 152 int l,r; 153 scanf("%d%d%d",&l,&r,&s[i].k); 154 s[i].l=min(l,r); 155 s[i].r=max(l,r); 156 s[i].di=i; 157 } 158 root=NULL; 159 solve(); 160 for (int i=1;i<=m;i++) printf("%d\n",ans[i]); 161 }