题意:
给数据,然后查询目标区间的和(查询区间内的数不能相同)
如:区间[1,4]值为1,1,2,3, 则和为1+2+3=6
然后在网上看了题解才略懂离线的意思
先表述一下我理解的离线吧:
就是先存储数据,然后再进行操作。而这道题就是先存储数据,然后在数据更新的同时进行区间的值的查找。
如果看起来懵的话,再看看大佬的解释吧
日常膜拜:ino
一、在线算法
在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。相对的,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。例如,选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必。
因为在线算法并不知道整个的输入,所以它被迫做出的选择最后可能会被证明不是最优的,对在线算法的研究主要集中在当前环境下怎么做出选择。对相同问题的在线算法和离线算法的对比分析形成了以上观点。如果想从其他角度了解在线算法可以看一下
流算法(关注精确呈现过去的输入所使用的内存的量),动态算法(关注维护一个在线输入的结果所需要的时间复杂度)和在线机器学习。
一个很好的展示在线算法概念的例子是加拿大旅行者问题,这个问题的目标是在一个有权图中以最小的代价到达一个目标节点,但这个有权图中有些边是不可靠的,可能已经被剔除。然而一个旅行者只有到某个边的一个端点时才能确定该边是否已经被移除了。最坏情况下,该问题会变得简单,即所有的不确定的边都被移除该问题将会变成通常的最短路径问题。
二、离线算法
离线算法设计策略都是基于在执行算法前输入数据已知的基本假设,也就是说,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果,通常将这类具有问题完全信息前提下设计出的算法成为离线算法。
思路:
先将数据存储下来,对更新的值进行排序(由小到大),再按照区间的右端点排序(由小到大),更新数据后再对当前更新的位置进行判断,看是否是查询区间的右端点,若是则进行查询。
,大概就是这样了吧。。。
(搬运的)代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#pragma warning (disable :4996);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
const int maxn = 30010;
ll num[maxn << 2];
void update(int pos, int val, int le, int ri, int node) {
if (le == ri) {
num[node] = val;
return;
}
int t = (le + ri) >> 1;
if (pos <= t) update(pos, val, le, t, node << 1);
else update(pos, val, t + 1, ri, node << 1 | 1);
num[node] = num[node << 1] + num[node << 1 | 1];
}
ll query(int l, int r, int le, int ri, int node) {
if (l <= le && ri <= r) return num[node];
int t = (le + ri) >> 1;
ll ans = 0;
if (l <= t) ans += query(l, r, le, t, node << 1);
if (r>t) ans += query(l, r, t + 1, ri, node << 1 | 1);
return ans;
}
struct edge {
int num, num1, pos;
}B[maxn * 4];
bool cmp2(const edge &a, const edge &b) {
return a.num1<b.num1;
}
int vis[maxn], tmp[maxn], pre[maxn], A[maxn];
ll ans[maxn * 4];
int main() {
int T, n, m, a, b;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
memset(vis, 0, sizeof(vis));
memset(ans, 0, sizeof(ans));
memset(pre, -1, sizeof(pre));
memset(num, 0, sizeof(num));
for (int i = 0; i<n; i++) {
scanf("%d", &A[i]);
tmp[i] = A[i];
}
sort(tmp, tmp + n);
scanf("%d", &m);
for (int i = 0; i<m; i++) {
scanf("%d%d", &B[i].num, &B[i].num1);
B[i].pos = i;
}
//按照查询区间右端点排序
sort(B, B + m, cmp2);
for (int i = 0, j = 0; i<n; i++) {
int t = lower_bound(tmp, tmp + n, A[i]) - tmp;//二分查找数据的位置
if (vis[t]) {//数字曾出现过
//pre为之前出现过的数字的位置
//出现过则清零之前位置的数字
update(pre[t], 0, 1, n, 1);
//i+1为当前位置
update(i + 1, A[i], 1, n, 1);
pre[t] = i + 1;
}
else {
vis[t] = 1;
update(i + 1, A[i], 1, n, 1);
pre[t] = i + 1;
}
for (; j<m; j++) {
//若当前位置为查询区间的右端点,进行查询
if (i + 1 != B[j].num1) break;
ans[B[j].pos] = query(B[j].num, B[j].num1, 1, n, 1);
}
}
for (int i = 0; i<m; i++) printf("%I64d\n", ans[i]);
}
return 0;
}