1036 [HEOI2012]采花 树状数组 离线操作 区间内出现两次以上数的数量
链接:https://ac.nowcoder.com/acm/problem/20545
来源:牛客网
题目描述
萧芸斓是Z国的公主,平时的一大爱好是采花。
今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。
花园足够大,容纳了 nnn 朵花,花有 ccc 种颜色(用整数 1−c1-c1−c 表示),且花是排成一排的,以便于公主采花。
公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。
由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 mmm 个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。
公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。
由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 mmm 个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。
输入描述:
第一行三个空格隔开的整数 n、cn、cn、c 以及 mmm。
接下来一行 nnn 个空格隔开的整数,每个数在[1, c]间,第 iii 个数表示第 iii 朵花的颜色。
接下来 mmm 行每行两个空格隔开的整数 lll 和 r(l≤r)r(l ≤ r)r(l≤r),表示女仆安排的行程为公主经过第 lll 到第 rrr 朵花进行采花。
输出描述:
共 mmm 行,每行一个整数,第 iii 个数表示公主在女仆的第 iii 个行程中能采到的花的颜色数。
示例1
备注:
本题采用多测试点捆绑测试,共有两个子任务。
对于子任务1,分值为100 分,保证 1≤n,c,m≤3×1051 \leq n, c, m \leq 3 \times 10^51≤n,c,m≤3×105。
对于子任务2,分值为100 分,保证 1≤n,c,m≤2×1061 \leq n, c, m \leq 2 \times 10^61≤n,c,m≤2×106。
对于全部的测试点,保证 1≤xi≤c1 \leq x_i \leq c1≤xi≤c,1≤l≤r≤n1 \leq l \leq r \leq n1≤l≤r≤n。
分析
题意是一个区间内同一种花出现两次,就算是采到了一种花
感觉很难维护,因为每一种花都在不同位置,每一种花也不好确定在一个区间内出现过两次
但思考假如 同一种花,第一朵出现在 i 位置,第二朵出现在 j 位置,那对于左边界在[1,i] 右边界在 [j,n]内的所有区间,这种花都有一个贡献
可以维护每一种花第一次出现和第二次出现的位置,
考虑从左往右遍历,同时将所有查询离线操作按照右边界递增排序,每次找到一朵新的花,如果不是第一次出现,就在最近的一次出现 j 前面的区间[1,j] 内 +1,同时将上上次出现的位置k 前面的区间 [1,k] 内-1抵消上一次出现的 +1。
这样树状数组维护的就是 [1,j] 区间内每种花朵出现两次以上的次数。对于区间[l,r]出现两次以上次数的花,sum[r] - sum[l-1] 就是答案
//-------------------------代码---------------------------- //#define int ll const int N = 2e6+10; int n,m; int a[N],tr[N],ans[N],pre[N][2]; struct node { int l,r,id; bool operator<(const node x)const { return r<x.r; } }v[N]; void add(int x,int c) { for(int i = x;i<=n;i+=lowbit(i)) tr[i] += c; } int sum(int x) { int res = 0;for(int i = x;i;i-=lowbit(i)) res += tr[i];return res; } void solve() { int w; cin>>n>>w>>m; fo(i,1,n) cin>>a[i]; fo(i,1,m) { cin>>v[i].l >> v[i].r;v[i].id = i; } sort(v+1,v+1+m); for(int i = 1,j = 1;i<=n;i++) { if(pre[a[i]][0] == 0) { pre[a[i]][0] = i; } else if(pre[a[i]][1] == 0) { pre[a[i]][1] = i; add(pre[a[i]][0],1); } else { add(pre[a[i]][0],-1); add(pre[a[i]][1],1); pre[a[i]][0] = pre[a[i]][1]; pre[a[i]][1] = i; } while(v[j].r == i)ans[v[j].id] = sum(v[j].r) - sum(v[j].l-1),j++; } fo(i,1,m) cout<<ans[i]<<endl; } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------