bzoj2743(树状数组 / 莫队)

Description

萧芸斓是Z国的公主,平时的一大爱好是采花。

今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。花园足够大,容纳了n朵花,花有c种颜色(用整数1-c表示),且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了m个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。

Input

 

 第一行四个空格隔开的整数n、c以及m。接下来一行n个空格隔开的整数,每个数在[1, c]间,第i个数表示第i朵花的颜色。接下来m行每行两个空格隔开的整数l和r(l ≤ r),表示女仆安排的行程为公主经过第l到第r朵花进行采花。

Output

共m行,每行一个整数,第i个数表示公主在女仆的第i个行程中能采到的花的颜色数。

Sample Input

5 3 5
1 2 2 3 1
1 5
1 2
2 2
2 3
3 5

Sample Output

2
0 0 1 0
「样例说明」
询问[1, 5]:公主采颜色为1和2的花,由于颜色3的花只有一朵,公主不采;询问[1, 2]:颜色1和颜色2的花均只有一朵,公主不采;
询问[2, 2]:颜色2的花只有一朵,公主不采;
询问[2, 3]:由于颜色2的花有两朵,公主采颜色2的花;
询问[3, 5]:颜色1、2、3的花各一朵,公主不采。

HINT

「数据范围」

对于100%的数据,1 ≤ n ≤    10^6,c ≤ n,m ≤10^6。

 这个题和上一个题有点类似(bzoj1878),维护的一个表示区间内至少出现两次的数的种数的树状数组。

pre[i]表示上一个和i位置相同颜色花的位置,我们每次更新不再是update(i,1)

而是update(pre[i],1)即加上 上一个位置的相同颜色花的影响和update(pre[pre[i]],-1)去除上上一次相同颜色花的影响。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define PI atan(1.0) * 4
#define e 2.718281828
#define rp(i, s, t) for (i = (s); i <= (t); i++)
#define RP(i, s, t) for (i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a, b) memset(a, b, sizeof(a))
#define push_back() pb()
#define fastIn                    \
    ios_base::sync_with_stdio(0); \
    cin.tie(0);
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
inline void write(int n)
{
    if (n < 0)
    {
        putchar('-');
        n = -n;
    }
    if (n >= 10)
        write(n / 10);
    putchar(n % 10 + '0');
}
const int N = 1e6 + 7;
int n;
int a[N], ans[N], last[N], sum[N], pre[N];
struct node
{
    int l, r, num;
} p[N];
int lowbit(int x)
{
    return x & (-x);
}
void update(int i, int x)
{
    while (i <= n)
    {
        sum[i] += x;
        i += lowbit(i);
    }
}
int query(int i)
{
    int res = 0;
    while (i >= 1)
    {
        res += sum[i];
        i -= lowbit(i);
    }
    return res;
}
bool cmp(node p1, node p2)
{
    return p1.r < p2.r;
}
int main()
{
    n = read();
    int c = read(), m = read();
    int i, j;
    rp(i, 1, n)
    {
        a[i] = read();
        if (last[a[i]]!=0)
            pre[i] = last[a[i]];
        last[a[i]] = i;
    }
    rp(i, 1, m) p[i].l = read(), p[i].r = read(), p[i].num = i;
    sort(p + 1, p + 1 + m, cmp);
    int nowqueues = 1;
    rp(i, 1, n)
    {
        if (pre[pre[i]]!=0)
            update(pre[pre[i]], -1);
        if (pre[i]!=0)
            update(pre[i], 1);
        while (p[nowqueues].r == i)
        {
            ans[p[nowqueues].num] = query(p[nowqueues].r) - query(p[nowqueues].l - 1);
            nowqueues++;
        }
    }
    rp(i, 1, m)
        printf("%d\n", ans[i]);
    return 0;
}

还有一种做法,可以用莫队来做,也算是经典的做法,很牛皮的暴力,其实就是从小区间推大区间的做法。

具体有关莫队的思想可以参考这篇博文(https://www.cnblogs.com/muzu/articles/7708349.html

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define PI atan(1.0)*4
#define e 2.718281828
#define rp(i,s,t) for (i = (s); i <= (t); i++)
#define RP(i,s,t) for (i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define push_back() pb()
#define fastIn                    \
    ios_base::sync_with_stdio(0); \
    cin.tie(0);
using namespace std;
inline int read()
{
    int a=0,b=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            b=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        a=(a<<3)+(a<<1)+c-'0';
        c=getchar();
    }
    return a*b;
}
inline void write(int n)
{
    if(n<0)
    {
        putchar('-');
        n=-n;
    }
    if(n>=10)
        write(n/10);
    putchar(n%10+'0');
}
const int N=2e5+7;
struct node{
    int l,r,m,id;
    bool operator< (const node &others) const{
        return m==others.m?r<others.r:m<others.m;
    }
}p[N];
int a[N],sum[N*20],res[N];
int main(){
	int n=read(),i;
    rp(i,1,n)
        a[i]=read();
    int Size=sqrt(n),cnt=0;
    int m=read();
    rp(i,1,m){
        int x=read(),y=read();
        p[++cnt].l=x,p[cnt].r=y,p[cnt].id=i,p[cnt].m=(x-1)/Size+1;
    }
    sort(p+1,p+cnt+1);
    int ans=0,l=0,r=0;
    rp(i,1,cnt){
        while(l<p[i].l) {sum[a[l]]--;if(!sum[a[l]]) ans--;l++;}
        while(l>p[i].l) {l--,sum[a[l]]++;if(sum[a[l]]==1) ans++;}
        while(r<p[i].r) {r++,sum[a[r]]++;if(sum[a[r]]==1) ans++;}
        while(r>p[i].r) {sum[a[r]]--;if(!sum[a[r]]) ans--;r--;}
        res[p[i].id]=ans;
    }
    rp(i,1,cnt) printf("%d\n",res[i]);
	return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值