链接:http://acm.hdu.edu.cn/showproblem.php?pid=3333
题意:求区间不同数之和
首先我们发现询问的顺序对答案不会造成影响,那么我们可以考虑离线算法(将询问存下来,在以一种特定的顺序排序,来降低复杂度)。
这个题求不同的数之和,那么重复元素不会对ans造成影响,我们可以对询问的右端点升序排序,那么我们可以pos_k遍历arr数组,对(1,pos_k)这段区间我们维护他的元素唯一性(区间的元素没有重复的),怎么维护呢,考虑到对于一次询问 的左端点是向右边走的计算答案,我们只需要维护每个元素的离他最近即可, 那么对于每次询问满足(r<=pos_k)是不是相当于求sum[r]-sum[l-1];
具体实现:
#include<bits/stdc++.h>
#define LL long long
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
using namespace std;
const LL maxn = 3e4 + 19;
struct tree {
LL l, r,sum;
LL id;
}tr[maxn<<2],qur[maxn<<2];
LL arr[maxn],ans[maxn<<2];
void push_up(LL pos)
{
tr[pos].sum = tr[lson(pos)].sum + tr[rson(pos)].sum;
}
void build(LL l, LL r, LL pos)
{
tr[pos].l = l; tr[pos].r = r;
tr[pos].sum = 0;
if (l == r)return;
int mid = l + r >> 1;
build(l, mid, lson(pos));
build(mid + 1, r, rson(pos));
}
void update(LL pos_in,LL pos_val, LL pos)//单点更新
{
if (tr[pos].l == pos_in&&tr[pos].r == pos_in)
{
tr[pos].sum += pos_val;
return;
}
int mid = tr[pos].l + tr[pos].r >> 1;
if (mid >= pos_in) update(pos_in, pos_val, lson(pos));
else update(pos_in, pos_val, rson(pos));
push_up(pos);
}
LL ask(LL ll, LL rr, LL pos)
{
if (ll == tr[pos].l&&tr[pos].r == rr)return tr[pos].sum;
int mid = tr[pos].l + tr[pos].r >> 1;
if (mid >= rr) return ask(ll, rr, lson(pos));
else if (mid < ll) return ask(ll, rr, rson(pos));
else return ask(ll, mid, lson(pos)) + ask(mid + 1, rr, rson(pos));
}
int main()
{
LL t;
scanf("%lld", &t);
while (t--)
{
mp.clear();
LL n, q;
scanf("%lld", &n);
for (LL i = 1; i <= n; ++i)
scanf("%lld", &arr[i]);
build(1, n, 1);
scanf("%lld", &q);
for (LL i = 1; i <= q; ++i)
{
scanf("%lld%lld", &qur[i].l, &qur[i].r);
qur[i].id = i;
}
sort(qur + 1, qur + q + 1, [](tree&a, tree&b) {
if (a.r == b.r)return a.l < b.l;
return a.r < b.r;
});
LL pos_k = 1;
map<LL, LL>mp;//ma[a] = pos表示值为a的元素在arr数组中的最后出现的位置;
for (LL i = 1; i <= q; ++i)
{
for (; pos_k <= qur[i].r; ++pos_k)
{
if (mp.find(arr[pos_k]) != mp.end())//如果当前的元素在之前已经出现了,更新sum[mp[arr[pos_k]]]~sum[pos_k的值(减去arr[pos_k]);
update(mp[arr[pos_k]], -arr[pos_k], 1);
mp[arr[pos_k]] = pos_k;
update(pos_k, arr[pos_k], 1);//更新sum[pos_k];
}
ans[qur[i].id] = ask(qur[i].l, qur[i].r, 1);
}
for (LL i = 1; i <= q; ++i)
printf("%lld\n", ans[i]);
}
return 0;
}
莫队算法:
这就是个裸的莫队算法;
但是,这个重复元素的标记,因为元素很大,如果用map,那会TLE的。。。我试过,,,,所以要离散化元素,再去重,那么每个元素所唯一对应一个下标,那么我们就可以愉快的用数组标记了。。。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 9;
struct node {
int l, r;
int id, conid;
}le[maxn];
int arr[maxn];
long long ans[maxn];
int pos[maxn];
int temp[maxn];
int mp[maxn];
bool cmp(node &a, node &b)
{
if (a.conid == b.conid)
return a.r < b.r;
return a.conid < b.conid;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
memset(mp, 0, sizeof mp);
int n, q;
scanf("%d", &n);
int len = sqrt(n)+2;
for (int i = 1; i <= n; ++i)
{
scanf("%d", &arr[i]);
temp[i] = arr[i];
}
sort(temp + 1, temp + n + 1);
int k = unique(temp + 1, temp + n + 1) - temp;
for (int i = 1; i <= n; ++i)
pos[i] = lower_bound(temp + 1, temp + k-1, arr[i]) - temp;
scanf("%d", &q);
for (int i = 1; i <= q; ++i)
{
scanf("%d%d", &le[i].l, &le[i].r);
le[i].id = i;
le[i].conid = le[i].l / len;
}
sort(le + 1, le + q + 1, cmp);
long long sum = arr[1];
int L = 1, R = 1;
mp[pos[1]] = 1;
for (int i = 1; i <= q; ++i)
{
while (R < le[i].r)
{
R++;
if (mp[pos[R]] == 0)sum += arr[R];
mp[pos[R]]++;
}
while (L > le[i].l)
{
L--;
if (mp[pos[L]]==0)sum += arr[L];
mp[pos[L]]++;
}
while (R > le[i].r)
{
mp[pos[R]]--;
if (mp[pos[R]] == 0)sum -= arr[R];
R--;
}
while (L < le[i].l)
{
mp[pos[L]]--;
if (mp[pos[L]] == 0)sum -= arr[L];
L++;
}
ans[le[i].id] = sum;
}
for (int i = 1; i <= q; ++i)printf("%lld\n", ans[i]);
}
return 0;
}