P5072 [Ynoi2015] 盼君勿忘
第一道 Ynoi 也可能是最后一道了
题面的意思挺简洁,对于每一次询问的
首先我们可以想到,对于一个长度为
题目只有询问操作,没有修改,
当然这个是 Ynoi ,所以你需要更多的优化。
一开始我的思路是用个桶维护每一个数在当前询问的区间内出现的次数,然后用链表维护一下出现的数,然后直接遍历一遍链表用快速幂根据上面得出的结论计算输出答案,当然,A 是不可能 A 的,但是会 T。
我们知道快速幂的复杂度是
并且我们的链表在查询
这个预处理的部分实际上也用到了一个叫光速幂的知识点,他可以
令
这时我们就可以进行
令
令
所以
对于这道题目每一次的模数都是变化的,所以每一次询问都要重新预处理一下,然后就是愉快的莫队了。
code:
#include<bits/stdc++.h>
#define bug cout<<"cao"<<endl
#define int long long
#define N 100010
using namespace std;
int n,m,l,r,a[N],kc,ans[N],lx,bl[N];//bl标记当前点属于哪一块,lx标记链表最后一个元素的标号
int p1[N],p2[N],p,cnt[N],sum[N];//sum存放当前出现次数的数的和,cnt是当前数的出现次数,p1p2是光速幂预处理的数组,p是模数
struct sb{int l,r,p,bh;}e[N];//询问操作
struct lb{int next,pre;}e1[N];//链表
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}
inline void print(int x){if(x>=10)print(x/10);putchar(x%10+48);}
inline int cmp(sb a,sb b)//询问排序函数
{
if(bl[a.l]==bl[b.l])return a.r<b.r;
else return a.l<b.l;
}
inline void _insert(int x)//从链表中加入某点
{
e1[x].pre=lx;//当前点的前驱是上一个节点
e1[lx].next=x;//上一个节点的后继是当前点
lx=x;//替换当前点为链表最后的节点
return ;
}
inline void _delete(int x)//删除一个节点
{
if(lx==x)//如果是最后一个节点的话
{
e1[e1[x].pre].next=0;//当前点的前一个节点的后继标记为没有
lx=e1[x].pre;//最后一个节点的编号改为当前点的前驱
e1[x].next=e1[x].pre=0;//清空当前点存的信息
return ;//返回
}
e1[e1[x].pre].next=e1[x].next;//当前节点的前驱的后继为当前节点的后继
e1[e1[x].next].pre=e1[x].pre;//当前节点后继的前驱为当前节点的前驱
e1[x].next=e1[x].pre=0;//清空信息
return ;//返回
}
inline void add(int x)//加入当前点的贡献
{
if(cnt[x])//如果要是当前点有出现次数
{
sum[cnt[x]]-=x;//先减去之前的
if(!sum[cnt[x]])//如果要是当前出现次数的点没了
_delete(cnt[x]);//从链表中删除当前节点
}
cnt[x]++;//当前点的出现次数加一
if(!sum[cnt[x]])//如果要是当前出现次数的点之前没有
_insert(cnt[x]);//插入当前点
sum[cnt[x]]+=x;//累加
}
inline void del(int x)//删除当前点的贡献
{
sum[cnt[x]]-=x;//首先减去当前点之前的贡献
if(!sum[cnt[x]])//如果要是sum是0了,说明没有当前出现次数的数
_delete(cnt[x]);//从链表中删除
cnt[x]--;//出现次数减一
if(cnt[x])//如果当前的数在序列中出现
{
if(!sum[cnt[x]])//如果要是没有当前出现次数的元素
_insert(cnt[x]);//插入链表中
sum[cnt[x]]+=x;//直接累加到当前sum数组上
}
}
inline int pw(int x){return p1[x%kc]*p2[x/kc]%p;}//利用预处理出的两个数组来o(1)查询
signed main()
{
n=read();m=read();
kc=sqrt(n);//计算块长
for(int i=1;i<=n;i++)
a[i]=read(),bl[i]=(i-1)/kc+1;//输入数据的同时处理出每一个数所属的块
for(int i=1;i<=m;i++)
e[i].l=read(),e[i].r=read(),e[i].p=read(),e[i].bh=i;//输入查询的信息并存编号
sort(e+1,e+m+1,cmp);//对询问进行排序
for(int nl=1,nr=0,i=1;i<=m;i++)
{
while(nr<e[i].r)add(a[++nr]);//正常莫队板子
while(nl>e[i].l)add(a[--nl]);
while(nr>e[i].r)del(a[nr--]);
while(nl<e[i].l)del(a[nl++]);
p=e[i].p;//更新模数
// cout<<nl<<" "<<nr<<endl;
p1[0]=p2[0]=1;//光速幂预处理
for(int j=1;j<=kc;j++)
p1[j]=p1[j-1]*2%p;
p2[1]=p1[kc];
for(int j=2;j<=kc;j++)
p2[j]=p2[j-1]*p2[1]%p;
for(int j=e1[0].next;j;j=e1[j].next)//利用链表来遍历每一个出现次数的数的和
{
ans[e[i].bh]+=sum[j]*(pw(e[i].r-e[i].l+1)-pw(e[i].r-e[i].l+1-j))%p;//计算答案,调用光速幂
ans[e[i].bh]=(ans[e[i].bh]+p)%p;//取模,保证是个非负数
}
}
for(int i=1;i<=m;i++)//遍历一遍询问输出答案
print(ans[i]),cout<<"\n";
return 0;
}
这个地方把排序的里面的 bl 数组预处理出来忘调用了。。。然后块长设的 400 T 飞了。。。
作者: 北烛青澜
出处:https://www.cnblogs.com/Multitree/p/17254456.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!