wannafly 练习赛10 f 序列查询(莫队,分块预处理,链表存已有次数)
链接:https://www.nowcoder.net/acm/contest/58/F
时间限制:C/C++ 5秒,其他语言10秒
64bit IO Format: %lld
题目描述
这个区间内一共有2^(r-l+1)-1个非空子序列
一个子序列对答案的贡献是其去重后的和
求所有子序列的贡献的和%p
每次的p不一样
输入描述:
第一行两个数n,m
第二行n个数表示序列a
后面m行每行三个数l,r,p表示查询区间[l,r],模数是p
输出描述:
对于每个查询输出一行一个数表示答案
输入例子:
5 5
1 2 2 3 4
1 2 233333
2 3 333333
1 5 203
3 5 15
2 4 8
输出例子:
6
6
176
6
0
-->
输入
5 5
1 2 2 3 4
1 2 233333
2 3 333333
1 5 203
3 5 15
2 4 8
输出
6
6
176
6
0
说明
[1,2]中有3个子序列(1),(2),(1,2),贡献分别为1,2,3
[2,3]中有3个子序列(2),(2),(2,2),贡献分别为2,2,2
[1,5]中有31个子序列
(1),(2),(2),(3),(4),(1,2),(1,2),(1,3),(1,4),(2,2),(2,3),(2,4),(2,3),(2,4),(3,4),(1,2,2),(1,2,3),(1,2,4),(1,2,3),
(1,2,4),(1,3,4),(2,2,3),(2,2,4),(2,3,4),(2,3,4),(1,2,2,3),(1,2,2,4),(1,2,3,4),(1,2,3,4),(2,2,3,4),(1,2,2,3,4)
贡献为:1,2,2,3,4,3,3,4,5,2,5,6,5,6,7,3,6,7,6,7,8,5,6,9,9,6,7,10,10,9,10
[3,5]中有7个子序列
(2),(3),(4),(2,3),(2,4),(3,4),(2,3,4)
贡献为:2,3,4,5,6,7,9
[2,4]中有7个子序列
(2),(2),(3),(2,2),(2,3),(2,3),(2,2,3)
贡献为:2,2,3,2,5,5,5
备注:
对于100%的数据,有1 <= n , m , a[i] <= 100000 , 1 <= p <= 1000000000
看这个题真的是一脸懵逼,看了题解之后算有所收获
贴个题解
/////////////////////////////////////////
先考虑单次询问怎么算
对于数x,假设出现了y次,区间长度是len
则x对答案的贡献是
是除了x之外的数有这么多个不同的子序列,这些对x的贡献没有影响
是所有x构成的子序列中有种至少包含一个x,有1种不包含x
如果每次模数一样的话,直接边跑莫队维护就可以了。。。
然而出题人想恶心你,所以每次模数不一样
注意到贡献分为两部分与
其中第一部分非常好维护
第二部分的贡献,可以把出现次数相同的数一起维护贡献
注意到一起区间中只有O( sqrt( n ) )种不同的出现次数
因为1+2+...+sqrt(n) = O(n)
这是一个自然根号
所以我们可以用一个均摊的莫队来维护区间可能的出现次数,从而维护区间中所有出现次数
然后为了O(1)实现快速幂,我们可以每次O( sqrt(n) )算出
2^1,2^2…2^sqrt(n) % p
以及2^sqrt(n),2^2sqrt(n)…2^sqrt(n)*sqrt(n) % p
总复杂度O( nsqrtm + msqrtn ) = O( msqrtn )
//////////////////////////////////////
再贴个自己代码
#include <bits/stdc++.h>
#define mst(a,b) memset((a),(b), sizeof a)
#define lowbit(a) ((a)&(-a))
#define IOS ios::sync_with_stdio(0);cin.tie(0);
using namespace std;
typedef long long ll;
const int mod=1e9+;
const int maxn=1e5+;
int qpow(int b,int p){
ll ret=;ll a=;
while(b){
if(b&)ret=ret*a%p; b>>=; a=a*a%p;
}
return (int)ret;
}
struct node{
int l,r,p,blo,id;
bool operator<(const node&p)const{
return (blo<p.blo||blo==p.blo&&r<p.r);
}
}qry[maxn]; int pre[maxn],nx[maxn];int head;
void insert(int v){
pre[v]=;
pre[head]=v;
nx[v]=head;
head=v;
}
void erase(int v){
if(!pre[v]){
head=nx[v];pre[head]=;
}else{
nx[pre[v]]=nx[v];
pre[nx[v]]=pre[v];
}
} int a[maxn];int sq; int one[maxn],two[maxn];
void init(int p){
one[]=;for(int i=;i<=sq+;++i)one[i]=one[i-]*%p;
two[]=;two[]=one[sq];
for(int i=;i<=sq+;++i)two[i]=(ll)two[i-]*two[]%p;
} ll tol[maxn];
int len;
int ti[maxn],cnt[maxn];
void update(int pos,int d){
int val=a[pos]; if(ti[val]){
tol[ti[val]]-=val;
--cnt[ti[val]];
if(!cnt[ti[val]])erase(ti[val]);
}
ti[val]+=d;
if(ti[val]){
tol[ti[val]]+=val;
if(!cnt[ti[val]])insert(ti[val]);
++cnt[ti[val]];
}
}
int get(int d,int p){
return (int)((ll)two[d/sq]*one[d%sq]%p);
}
int get_ans(int p){
ll ret=,all=;
for(int i=head;i;i=nx[i]){
ret=(ret-tol[i]*get(len-i,p))%p;
all+=tol[i];
}
ret=(ret+all*qpow(len,p))%p;
return (int)((ret+p)%p);
}
int ans[maxn];
int main(){
#ifdef local
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
#endif
int n,m;scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)scanf("%d",&a[i]);
sq=sqrt(n);
for(int i=;i<=m;++i){
scanf("%d%d%d",&qry[i].l,&qry[i].r,&qry[i].p);
qry[i].blo = qry[i].l/sq;qry[i].id=i;
}
sort(qry+,qry++m);
int L=,R=;
for(int i=;i<=m;++i){
init(qry[i].p);
len=qry[i].r-qry[i].l+; while(R<qry[i].r){++R;update(R,);}
while(R>qry[i].r){update(R,-);--R;} while(L>qry[i].l){--L;update(L,);}
while(L<qry[i].l){update(L,-);++L;}
// cout<<qry[i].l<<" "<<qry[i].r<<endl;
// for(int i=head;i;i=nx[i])cout<<i<<" ";
// cout<<endl<<endl;
ans[qry[i].id]=get_ans(qry[i].p);
}
for(int i=;i<=m;++i)printf("%d\n",ans[i]);
return ;
}
这题的收获主要是在那个sqrt(n)预处理从而o1求出 2^(len-y)%p 那里
之前是做过类似的题目的,然后记忆中一直都是分块分块,很模糊,这次遇到这题才惊叹,哦原来是这么用,还有就是最多只有sqrt(n)种次数,也不知道怎么存,看了别人代码,发现链表完美符合要求,真的是牛批,自己还是太弱了emmm
(这题插入链表开始的时候写错了,调了几个小时,以后要细心点)
wannafly 练习赛10 f 序列查询(莫队,分块预处理,链表存已有次数)的更多相关文章
- 【BZOJ 3809】 3809: Gty的二逼妹子序列 (莫队+分块)
3809: Gty的二逼妹子序列 Time Limit: 80 Sec Memory Limit: 28 MBSubmit: 1728 Solved: 513 Description Autumn ...
- BZOJ 3809 Gty的二逼妹子序列(莫队+分块)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3809 [题目大意] 给定一个长度为n(1<=n<=100000)的正整数序 ...
- BZOJ_3809_Gty的二逼妹子序列 && BZOJ_3236_[Ahoi2013]作业 _莫队+分块
BZOJ_3809_Gty的二逼妹子序列 && BZOJ_3236_[Ahoi2013]作业 _莫队+分块 Description Autumn和Bakser又在研究Gty的妹子序列了 ...
- 【BZOJ4540】【HNOI2016】序列(莫队)
[BZOJ4540][HNOI2016]序列(莫队) 题面 BZOJ 洛谷 Description 给定长度为n的序列:a1,a2,-,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是 ...
- 【BZOJ5301】【CQOI2018】异或序列(莫队)
[BZOJ5301][CQOI2018]异或序列(莫队) 题面 BZOJ 洛谷 Description 已知一个长度为 n 的整数数列 a[1],a[2],-,a[n] ,给定查询参数 l.r ,问在 ...
- BZOJ4540 [Hnoi2016]序列 【莫队 + ST表 + 单调栈】
题目 给定长度为n的序列:a1,a2,-,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,-,ar- 1,ar.若1≤l≤s≤t≤r≤n,则称a[s:t]是a[ ...
- 莫队+分块 BZOJ 3809
3809: Gty的二逼妹子序列 Time Limit: 80 Sec Memory Limit: 28 MBSubmit: 1634 Solved: 482[Submit][Status][Di ...
- [AHOI2013]作业 (莫队+分块)
[AHOI2013]作业 (莫队+分块) 题面 给定了一个长度为n的数列和若干个询问,每个询问是关于数列的区间[l,r],首先你要统计该区间内大于等于a,小于等于b的数的个数,其次是所有大于等于a,小 ...
- [BZOJ3585]mex(莫队+分块)
显然可以离线主席树,这里用莫队+分块做.分块的一个重要思想是实现修改与查询时间复杂度的均衡,这里莫队和分块互相弥补. 考虑暴力的分块做法,首先显然大于n的数直接忽略,于是将值域分成sqrt(n)份,每 ...
随机推荐
- 【6.12校内test】T2 子集
这道题大概是这三道题里最简单的啦 但这阻止不了我废的脚步 [问题描述] 对于 n=4 时,对应的集合 s={4,3,2,1},他的非空子集有 15 个依次如下: {1} {2} {1,2} {3} { ...
- Deepin安装 ruby 包管理工具 RVM(适用于 Debian 系列)
1. 安装 GPG keys 先安装 gpg2 工具 sudo apt install gnupg2 再安装 keys gpg2 --recv-keys 409B6B1796C275462A17031 ...
- AWS EC2 搭建 Hadoop 和 Spark 集群
前言 本篇演示如何使用 AWS EC2 云服务搭建集群.当然在只有一台计算机的情况下搭建完全分布式集群,还有另外几种方法:一种是本地搭建多台虚拟机,好处是免费易操控,坏处是虚拟机对宿主机配置要求较高, ...
- 091、万能的数据收集器 Fluentd (2019-05-15 周三)
参考https://www.cnblogs.com/CloudMan6/p/7798224.html 前面的ELK 中我们使用的是 Filebeat 收集Docker日志,利用的是默认的loggi ...
- 吴恩达机器学习103:SVM之大间隔分类器的数学原理
1.向量内积: (1)假设有u和v这两个二维向量:,接下来看一下u的转置乘以v的结果,u的转置乘以v也叫做向量u和向量v的内积,u是一个二维向量,可以将其在图上画出来,如下图所示向量u: 在横轴上它的 ...
- Linux系统,无法收到组播消息的问题
解决办法: 做以下3个修改 1. /etc/sysctl.conf 文件中的 net.ipv4.conf.all.rp_filter 设置成0 2. sysctl -w net.ipv4.conf.a ...
- linux误卸载openssl后的恢复
一.原因 由于在编译mysql时,报ssl错误,于是想卸载openssl再重新安装 rpm -qa|grep openssl rpm -e openssl-.el7_6..x86_64 rpm -e ...
- python打印实心等边三角形和空心等边三角形
#1 打印实心等边三角形 n = 5 for i in range(1, n+1): # 控制三角形的高,也就是层数 for k in range(2*(n-i)): # 控制每层第一个*的空格,从最 ...
- linux基础命令--lsof
lsof(list open files)作用: 是一个列出当前系统打开文件的工具. 注: 在终端下输入lsof即可显示系统打开的文件,因为 lsof 需要访问核心内存和各种文件,所以必须以 root ...
- 移动端h5模拟长按事件
为啥写这篇文章 最近接了个需求,要求长按某个标签显示删除一个悬浮的删除按钮.这个需求其实在app上很常见,但是在移动端h5中,我们没有长按的事件,所以就需要自己模拟这个事件了. 大概效果如下: ps: ...