您的位置:首页 > 理论基础 > 数据结构算法

【HDU5654 BestCoder Round 77 (div1) D】【前驱位置思想 排序 树状数组】xiaoxin and his watermelon candy 区间内多少个不同连续单升三元

2016-03-27 22:51 549 查看


xiaoxin and his watermelon candy

 Accepts: 15
 
 Submissions: 172

 Time Limit: 4000/4000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)

问题描述
六年级的暑假,在腾讯实习的xiaoxin巨从leader那得到了很多西瓜糖。每个西瓜糖都有一个属性代表它的甜度值,用一个正整数表示。

xiaoxin从小就特别会玩,他先把所有糖果排成一条直线,每次从一个区间内选出三个连续的且甜度值不减的西瓜糖来吃,即:
如果他选择了三元组 (a_i, a_j, a_k)(a​i​​,a​j​​,a​k​​) 那么:
1.     j = i + 1, k = j + 1j=i+1,k=j+1
2.	a_i \leq a_j \leq a_ka​i​​≤a​j​​≤a​k​​

好奇的xiaoxin想知道每次吃糖果,他有多少种吃法?
我们认为两个三元组 (a_0, a_1, a_2), (b_0, b_1, b_2)(a​0​​,a​1​​,a​2​​),(b​0​​,b​1​​,b​2​​) 是不同的,当且仅当:
a_0 \neq b_0a​0​​≠b​0​​ or a_1 \neq b_1a​1​​≠b​1​​ or a_2 \neq b_2a​2​​≠b​2​​.

输入描述
多组测试数据, 第一行为一个整数(T \leq 10T≤10)表示测试数据组数。

对于每组测试数据,第一行是一个整数 n(1 \leq n \leq 200,000)n(1≤n≤200,000) 表示西瓜糖的个数。接下来 nn个数表示每个西瓜糖的甜度值(0 \leq a_i \leq 1000,000,0000≤a​i​​≤1000,000,000)。接下来有 Q(1 \leq 200,000)Q(1≤200,000) 行代表询问的次数,每个询问用两个整数 l, r(1\leq l \leq r \leq n)l,r(1≤l≤r≤n) 表示。

输出描述
对于每次询问,输出一个数,表示这次xiaoxin的吃法的数目。

输入样例
1
5
1 2 3 4 5
3
1 3
1 4
1 5

输出样例
1
2
3


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2e5 + 10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
int c
;				//用于记录原数的数组
int b
;				//树状数组,我们利用其来统计在某个范围内有多少个数字的前驱<l
int nxt
;				//nxt[i]表示与c[i]权值相同的下一个数的位置
int l
, r
, o
;	//用于处理询问的数组
int ans
;				//用于输出答案的数组
map< pair<int, pair<int, int> >, int>mop;
map< pair<int, pair<int, int> >, int>::iterator it;
bool cmp(int x, int y)
{
return l[x] < l[y];
}
int check(int x)
{
int ret = 0;
for (; x; x -= x&-x)ret += b[x];
return ret;
}
void add(int x, int val)
{
for (; x <= n; x += x&-x)b[x] += val;
}
int main()
{
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
mop.clear();
scanf("%d", &n);
for (int i = 1; i <= n; ++i)b[i] = nxt[i] = 0;
for (int i = 1; i <= n; ++i)scanf("%d", &c[i]);
for (int i = 3; i <= n; ++i)
{
if (i >= 3 && c[i - 2] <= c[i - 1] && c[i - 1] <= c[i])
{
int x = mop[MP(c[i - 2], MP(c[i - 1], c[i]))];
if (x == 0)add(i, 1);
else nxt[x] = i;
mop[MP(c[i - 2], MP(c[i - 1], c[i]))] = i;
}
}
scanf("%d", &m);
for (int i = 1; i <= m; ++i)scanf("%d%d", &l[i], &r[i]), o[i] = i;
sort(o + 1, o + m + 1, cmp);
int p = 1;
for (int i = 1; i <= m; ++i)
{
int lft = l[o[i]];
int rgt = r[o[i]];
while (p <= lft + 1)
{
if(nxt[p])add(nxt[p], 1);
++p;
}
if (rgt - lft >= 2)ans[o[i]] = check(rgt) - check(lft + 1);
else ans[o[i]] = 0;
}
for (int i = 1; i <= m; ++i)printf("%d\n", ans[i]);
}
return 0;
}
/*
【trick&&吐槽】
1,注意,我们一定要注意我们对于一个[l,r]的询问区间,其所对应的真实区间其实是[l+2,r]
2,边界问题把握好,比如——
if (rgt - lft >= 2)ans[o[i]] = check(rgt) - check(lft + 1);
else ans[o[i]] = 0;
这第二句不能忽视
3,复杂度固然重要,但是代码复杂度也非常重要,否则实现不了程序再快又有什么用

【题意】
问你一个区间中有多少个本质不同的连续不下降三元组
即(ai,aj,ak)满足i+1=j,j+1=k且a[i]<=a[j]<=a[k]

【类型】
树状数组 排序

【分析】
我们要统计的是区间范围[l,r]内有多少个三元组的pre小于l。
为了实现这个功能,一个好的做法是,在数据结构中,只维护所有pre<l的数。
显然,随着查询区间左界l的增大,pre<l的数是增多的。
于是,我们队所有的查询按照左界做排序。
我们之前会把pre<l的所有数都放进位置恰好其下标所对应的数据结构(如树状数组)中。
那我们查询的时候,只要查询[lft+2,rgt]期间有多少个数即可

【时间复杂度&&优化】
O(nlogn)

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐