HDU 6959 zoto (莫队+树状数组)

该博客主要讨论了一种算法问题,即在给定序列上进行m次询问,每次询问[x1, x2]区间内大小位于[y1, y2]的不同的数的数量。这个问题可以通过维护权值线段树或树状数组来解决,实现单点修改和区间查询的功能。博客详细介绍了算法思路,包括如何使用树状数组进行区间更新和单点查询,并给出了完整的C++代码实现。该问题的时间复杂度为O(Nsqrt(N)logN),但实际运行时间可能更短。
摘要由CSDN通过智能技术生成

传送门

题意本质就是,给定序列,m次询问,每次问[x1,x2]这个区间内的数中,大小位于[y1,y2]这个区间的不同的数有多少个。
求区间不同的数的加强版 。
求区间不同的数的时候,我们只需要维护1个桶,记录每个数出现多少次即可,用一个变量ret保存答案,这里我们需要对每个数都分别保存答案,可以用权值线段树或树状数组维护 。
单点修改,区间查询。

时间复杂度是O(Nsqrt(N)logN) 但是跑不满,因为不一定每次都要修改。

1900ms。

注意我们把y的坐标都加了1,保证树状数组维护的定义域是从1开始。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//平面上若干个点 m次询问 每次问一个矩形内 不同的y坐标有多少个
//莫队 维护树状数组 指针在x轴移动 树状数组维护y轴的桶 表示这个y坐标能否贡献1  每次单点修改 区间查询
int n,c[maxn],m;
int a[maxn];
int ans[maxn];
int num[maxn];//当前区间内 y坐标为i的个数
int block;
struct node 
{
    int x0,y0,x1,y1;//[x0,x1]区间内 树状数组查[y0,y1]
    int id;
}q[maxn];
bool cmp(const node &a,const node &b) 
{
    return (a.x0/block)^(b.x0/block)?a.x0<b.x0:(((a.x0/block)&1)?a.x1<b.x1:a.x1>b.x1);
}
inline void upd(int x,int val)//更新函数
{
	for (; x <= maxn-1; x += (x&-x))//一路找x的父节点,并且更新父节点
	{
		c[x] += val;
	}
}
inline int ask(int x)//查询前缀和
{
	int res=0;
	for (; x >= 1; x -= (x&-x))
	{
		res += c[x];
	}
	return res;
}
inline void add(int x) 
{
    num[a[x]]++;
    if(num[a[x]]==1) upd(a[x],1);
}
inline void del(int x)
{
    num[a[x]]--;
    if(!num[a[x]]) upd(a[x],-1);
}
void init()
{
    memset(c,0,sizeof c);
    memset(num,0,sizeof num);
}
int main()
{
    int T;cin>>T;
    while(T--)
    {
        init();
        scanf("%d %d",&n,&m);
        block=sqrt(n);
        for(int i=1;i<=n;i++) a[i]=read()+1;
        for(int i=1;i<=m;i++) 
        {
            q[i].id=i;
            q[i].x0=read();
            q[i].y0=read()+1;
            q[i].x1=read();
            q[i].y1=read()+1;
        }
        sort(q+1,q+m+1,cmp);
        int l=0,r=0;
        for(int i=1;i<=m;i++)
        {
            int  ql=q[i].x0,qr=q[i].x1;
            while(l<ql) del(l++);
            while(l>ql) add(--l);
            while(r<qr) add(++r);
            while(r>qr) del(r--);
            ans[q[i].id]=ask(q[i].y1)-ask(q[i].y0-1);
        }
        for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值