传送门 : UVA 11235
题意
- 给定一个长为n的数列, 给定q个查询, 查询[l, r]内出现最多数字的出现次数
题解
- 常规解法: 线段树区间合并, 需要注意的是, 查询时的区间合并不能忽略
- 离散化,离散的运用:将连续的相等的一个区间的点集,聚集为线段树的一个节点,并且在这个节点上有信息能体现出这个区间的性质。
code:
/*
*adrui's submission
*Language : C++
*Reault : Accepted
*Favorite : Dragon Balls
*Love : ll
*
*Standing in the Hall of Fame
*/
//区间合并
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define debug 0
#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define inf 0x7f7f7f7f
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
const int maxn(1e5 + 7);
int n, q;
struct segment_tree
{
int l;
int r;
int ans, lAns, rAns; //l, r为左右端点值
//lAns, rAns为左右端连续的相同个数, ans为此区间ans
} node[maxn << 2];
int num[maxn];
void pushUp(segment_tree &q, segment_tree q1, segment_tree q2, int l, int r)
{
q.l = q1.l;
q.r = q2.r;
q.lAns = q1.lAns;
q.rAns = q2.rAns;
if(q1.r != q2.l) //直接维护ans
{
q.ans = max(q1.ans, q2.ans);
}
else //维护ans, lAns, rAns
{
if(q1.lAns == mid - l + 1)
q.lAns += q2.lAns;
if(q2.rAns == r - mid)
q.rAns += q1.rAns;
q.ans = max(q1.rAns + q2.lAns, max(q1.ans, q2.ans));
}
}
void build(int rt, int l, int r)
{
if( l == r)
{
cin >> node[rt].r;
node[rt].l = node[rt].r;
node[rt].ans = node[rt].lAns = node[rt].rAns = 1;
return;
}
build(ls);
build(rs);
pushUp(node[rt], node[rt << 1], node[rt << 1 | 1], l, r);
}
segment_tree query(int rt, int l, int r, int ql, int qr)
{
if(ql <= l && qr >= r)
{
return node[rt];
}
segment_tree q, q1, q2;
if(ql <= mid && mid < qr)//如果查询区间被mid分割, 则分开查询并合并
{
q1 = query(ls, ql, qr);
q2 = query(rs, ql, qr);
pushUp(q, q1, q2, l, r);
}
else //反之, 只有一个区间要查询
{
if(ql <= mid) q = query(ls, ql, qr);
if(qr > mid) q = query(rs, ql, qr);
}
return q;
}
int main()
{
#if debug
freopen("in.txt", "r", stdin);
#endif //debug
cin.tie(0);
cin.sync_with_stdio(false);
int a, b;
while(cin >> n, n)
{
cin >> q;
build(1, 1, n);//建树
while(q--)
{
cin >> a >> b;
cout << query(1, 1, n, a, b).ans << endl;
}
}
return 0;
}
/*
*adrui's submission
*Language : C++
*Reault : Accepted
*Favorite : Dragon Balls
*Love : ll
*
*Standing in the Hall of Fame
*/
//线段树 + 离散化
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define debug 0
#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define inf 0x7f7f7f7f
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
const int maxn(1e5 + 7);
int n, q;
struct{
int l;
int r;
int len;
}node[maxn << 2];
struct{
int l;
int r;
}seg[maxn];
int num[maxn], Hash[maxn];
void pushUp(int rt){
node[rt].len = max(node[rt << 1].len, node[rt << 1 |1].len);
}
void build(int rt, int l, int r){
if( l == r){
node[rt].len = seg[l].r - seg[l].l + 1;
return;
}
build(ls);
build(rs);
pushUp(rt);
}
int query(int rt, int l, int r, int ql, int qr){
if(ql <= l && qr >= r){
return node[rt].len;
}
int res = -inf;
if(ql <= mid) res = max(res, query(ls, ql, qr));
if(qr > mid) res = max(res, query(rs, ql, qr));
return res;
}
int main(){
#if debug
freopen("in.txt", "r", stdin);
#endif //debug
cin.tie(0);
cin.sync_with_stdio(false);
int a, b;
while(cin >> n, n){
cin >> q;
int cnt = 0, pre = inf;
for(int i = 1; i <= n; i++){
cin >> num[i];
if(num[i] != pre){
pre = num[i];
cnt++;
seg[cnt].l = i;
seg[cnt].r = i;
}else{
seg[cnt].r = i;
}
Hash[i] = cnt;//hash下用于记录当前点所在离散的下标
}
build(1, 1, cnt);//离散化建树
while(q--){
cin >> a >> b;
int pos1 = Hash[a], pos2 = Hash[b], ans;//hash获取
if(pos1 == pos2){//如果a, b在同一离散点内
ans = b - a + 1;
}
else{ //不同离散点, len1, len3分别为a, b所在离散点的连续长度,len2为中间离散点的最大ans
int len1 = seg[pos1].r - a + 1;
int len2 = 0;
int len3 = b - seg[pos2].l + 1;
if(pos2 ^ pos1 ^ 1){//a, b中间有离散点, 查询最大
len2 = query(1, 1, cnt, pos1 + 1, pos2 - 1);
}
ans = max(len1, max(len2, len3));
}
cout << ans << endl;
}
}
return 0;
}