P4587 [FJOI2016]神秘数
首先对于数
那么通过这个可以推出来一种暴力做法,每一次找到一个在
但是这样复杂度明显会非常高,考虑优化这个过程。
利用树状数组维护
可以很好的发现,每进行一次这个过程,
但是复杂度仍然不乐观,复杂度的根源在于每次建树状数组需要花费大量时间,所以考虑能否可持久化,那就可以打可持久化线段树,但是这里选择更简单的方法。
考虑对询问进行前缀和处理,每次需要查询的是
那么有很多区间的询问是重合的,就可以离线处理了。
先每次标记询问的位置,然后从前往后遍历整个序列,维护树状数组,每次再更新询问所得到的值,结束遍历后用前缀和处理每次的答案,与之前答案作对比,如果被更新了,那么继续迭代,直到无数值更新后,答案就统计出来了。
时间复杂度:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m,cnt,f[N],t[N],ans[N],sum[N],bef[N];
struct node
{
int name,data;
}a[N];
int cmp(node fi,node se)
{
return fi.data<se.data;
}
int cmp2(node fi,node se)
{
return fi.name<se.name;
}
struct node2
{
int name,l,r,sum;
}p[N];
int cmp3(node2 fi,node2 se)
{
return fi.r<se.r;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int k)
{
while(x<=cnt)
{
f[x]+=k;
x+=lowbit(x);
}
}
int search(int x)
{
int sum=0;
while(x)
{
sum+=f[x];
x-=lowbit(x);
}
return sum;
}
vector<int>lc[N],rc[N];
void diedai()
{
memset(f,0,sizeof(f));
for(int i=1;i<=m;i++)sum[i]=1;
bool flag=0;
for(int i=1;i<=n;i++)lc[i].clear(),rc[i].clear();
for(int i=1;i<=m;i++)lc[p[i].l-1].push_back(p[i].name);
for(int i=1;i<=m;i++)rc[p[i].r].push_back(p[i].name);
for(int i=1;i<=n;i++)
{
update(a[i].data,t[a[i].data]);
int len=lc[i].size();
for(int j=0;j<len;j++)
{
int l=1,r=cnt;
while(l<r)
{
int mid=(l+r+1)>>1;
if(t[mid]<=p[lc[i][j]].sum)l=mid;
else r=mid-1;
}
sum[lc[i][j]]-=search(l);
}
len=rc[i].size();
for(int j=0;j<len;j++)
{
int l=1,r=cnt;
while(l<r)
{
int mid=(l+r+1)>>1;
if(t[mid]<=p[rc[i][j]].sum)l=mid;
else r=mid-1;
}
sum[rc[i][j]]+=search(l);
}
}
for(int i=1;i<=m;i++)
{
bef[i]=p[i].sum;
if(sum[i]!=p[i].sum)flag=1;
p[i].sum=sum[i];
}
if(flag)diedai();
}
signed main()
{
freopen("niuniunum.in","r",stdin);
freopen("niuniunum.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i].data),a[i].name=i;
sort(a+1,a+1+n,cmp);
int bef=-1;
for(int i=1;i<=n;i++)
{
if(a[i].data!=bef)cnt++,t[cnt]=a[i].data;
bef=a[i].data;
a[i].data=cnt;
}
sort(a+1,a+1+n,cmp2);
for(int i=1;i<=n;i++)update(a[i].data,a[i].data);
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
p[i].name=i;
scanf("%lld%lld",&p[i].l,&p[i].r);
p[i].sum=1;
}
diedai();
for(int i=1;i<=m;i++)printf("%lld\n",p[i].sum);
return 0;
}
/*
8 6
1 2 3 4 5 17 1 99
1 5
2 5
1 6
1 7
1 8
3 8
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)