题意

给定一个长度为 \(n\) 的序列 \(a\) 和 \(m\) 次询问,第 \(i\) 次询问需要求出 \([l_i,r_i]\) 内所有子序列去重之后的和,对 \(p_i\) 取模。

\(\texttt{Data Range:}1\leq n,m,a_i\leq 10^5,1\leq p_i\leq 10^9\)

题解

人生第一道 Ynoi,写篇题解祭之。

我们与其考虑某个子序列包含了哪些值,还不如看某个值能贡献到多少个子序列。

然而正着做不好做,因为一个子序列中某个值可能出现多次,所以考虑反过来看一个值不能贡献到多少个子序列,再用总的减去这个子序列就好了。

考虑某一个 \([l,r]\) 的询问,其中某个数出现了 \(k\) 次,可以很容易由上面的分析知道这个数将会对 \(2^{r-l+1}-2^{r-l+1-k}\) 个子序列产生贡献。

同时,注意到多个出现次数相同的数可以一起加起来贡献。所以我们可以考虑设 \(b_k\) 为当前的区间内出现了 \(k\) 次的所有数的和,那么我们可以得到答案为

\[\sum\limits_{k}b_k\left(2^{r-l+1}-2^{r-l+1-k}\right)
\]

注意到 \(b_k\) 可以使用莫队来维护,所以我们就得到了一个 \(O(nm\log n)\) 的算法,但是无法通过。

考虑统计答案的时候我们会枚举很多等于 \(0\) 的 \(b_k\)。所以我们在移动区间端点的时候可以同时用链表记录一下满足 \(b_k\neq 0\) 的那些 \(k\)。

注意到链表中记录的 \(k\) 是 \(O(\sqrt{n})\) 的,所以时间复杂度就变为 \(O(m\sqrt{n}\log n)\),还是会 TLE。

注意到这个 \(\log\) 甚至都可以搞掉,考虑分块打表,对于每个询问都预处理一次,因为一次预处理是 \(O(\sqrt{n})\) 的,所以复杂度为 \(O(m\sqrt{n})\),可过。

代码

#include<bits/stdc++.h>
#pragma GCC optimize("Ofast,unroll-loops")
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51;
struct Query{
ll l,r,p,id;
inline bool operator <(const Query &rhs)const;
};
Query qry[MAXN];
ll n,qcnt,l,r,p,blockSize,ptrl,ptrr,len,hd;
li rres;
ll x[MAXN],res[MAXN],cntl[MAXN],sum[MAXN],prv[MAXN],nxt[MAXN];
ll blk[MAXN],pw[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline bool Query::operator <(const Query &rhs)const
{
if(l/blockSize==rhs.l/blockSize)
{
return l/blockSize==1?r<rhs.r:r>rhs.r;
}
return l<rhs.l;
}
inline void insert(ll x)
{
prv[x]=0,nxt[x]=hd,prv[hd]=x,hd=x;
}
inline void erase(ll x)
{
if(x==hd)
{
return (void)(hd=nxt[x],prv[nxt[x]]=prv[x]=nxt[x]=0);
}
nxt[prv[x]]=nxt[x],prv[nxt[x]]=prv[x],prv[x]=nxt[x]=0;
}
inline void add(ll pos)
{
if(!(cntl[x[pos]]++))
{
sum[1]+=x[pos];
}
else
{
sum[cntl[x[pos]]-1]-=x[pos],sum[cntl[x[pos]]]+=x[pos];
}
if(sum[cntl[x[pos]]]==x[pos])
{
insert(cntl[x[pos]]);
}
if(!sum[cntl[x[pos]]-1])
{
erase(cntl[x[pos]]-1);
}
}
inline void del(ll pos)
{
if(!(--cntl[x[pos]]))
{
sum[1]-=x[pos];
}
else
{
sum[cntl[x[pos]]+1]-=x[pos],sum[cntl[x[pos]]]+=x[pos];
}
if(sum[cntl[x[pos]]]==x[pos])
{
insert(cntl[x[pos]]);
}
if(!sum[cntl[x[pos]]+1])
{
erase(cntl[x[pos]]+1);
}
}
inline void setup(ll md)
{
pw[0]=blk[0]=1;
for(register int i=1;i<=511;i++)
{
pw[i]=(pw[i-1]+pw[i-1])%md;
}
blk[1]=(pw[511]+pw[511])%md;
for(register int i=1;i<=512;i++)
{
blk[i]=(li)blk[i-1]*blk[1]%md;
}
}
inline ll query(ll x,ll md)
{
return (li)blk[x>>9]*pw[x&511]%md;
}
int main()
{
blockSize=sqrt(n=read()),qcnt=read();
for(register int i=1;i<=n;i++)
{
x[i]=read();
}
for(register int i=1;i<=qcnt;i++)
{
l=read(),r=read(),p=read(),qry[i]=(Query){l,r,p,i};
}
sort(qry+1,qry+qcnt+1),ptrl=1;
for(register int i=1;i<=qcnt;i++)
{
while(ptrr<qry[i].r)
{
add(++ptrr);
}
while(ptrr>qry[i].r)
{
del(ptrr--);
}
while(ptrl<qry[i].l)
{
del(ptrl++);
}
while(ptrl>qry[i].l)
{
add(--ptrl);
}
setup(p=qry[i].p),len=qry[i].r-qry[i].l+1,rres=0;
for(register int j=hd;j;j=nxt[j])
{
rres+=(li)sum[j]*(query(len,p)-query(len-j,p));
}
res[qry[i].id]=(rres%p+p)%p;
}
for(register int i=1;i<=qcnt;i++)
{
printf("%d\n",res[i]);
}
}

Luogu P5072 [Ynoi2015]盼君勿忘的更多相关文章

  1. 【题解】Luogu P5072 [Ynoi2015]盼君勿忘

    众所周知lxl是个毒瘤,Ynoi道道都是神仙题,题面好评 原题传送门 一看这题没有修改操作就知道这是莫队题 我博客里对莫队的简单介绍 既然是莫队,我们就要考虑每多一个数或少一个数对答案的贡献是什么 假 ...

  2. 洛谷:P5072 [Ynoi2015]盼君勿忘

    原题地址:https://www.luogu.org/problem/P5072 题目简述 给定一个序列,每次查询一个区间[l,r]中所有子序列分别去重后的和mod p 思路 我们考虑每个数的贡献.即 ...

  3. 洛谷P5072 [Ynoi2015]盼君勿忘 [莫队]

    传送门 辣鸡卡常题目浪费我一下午-- 思路 显然是一道莫队. 假设区间长度为\(len\),\(x\)的出现次数为\(k\),那么\(x\)的贡献就是\(x(2^{len-k}(2^k-1))\),即 ...

  4. P5072 [Ynoi2015]盼君勿忘

    传送门 一开始理解错题意了--还以为是两个子序列相同的话只算一次--结果是子序列里相同的元素只算一次-- 对于一个区间\([l,r]\),设其中\(x\)出现了\(k\)次,那么它的贡献就是它的权值乘 ...

  5. [Ynoi2015]盼君勿忘

    题目大意: 给定一个序列,每次查询一个区间\([l,r]\)中所有子序列分别去重后的和\(\bmod p\)(每次询问模数不同). 解题思路: 在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后 ...

  6. 【洛谷5072】[Ynoi2015] 盼君勿忘(莫队)

    点此看题面 大致题意: 一个序列,每次询问一个区间\([l,r]\)并给出一个模数\(p\),求模\(p\)意义下区间\([l,r]\)内所有子序列去重后值的和. 题意转化 原来的题意看起来似乎很棘手 ...

  7. Luogu5072 [Ynoi2015]盼君勿忘 【莫队】

    题目描述:对于一个长度为\(n\)的序列,\(m\)次询问\(l,r,p\),计算\([l,r]\)的所有子序列的不同数之和\(\mathrm{mod} \ p\). 数据范围:\(n,m,a_i\l ...

  8. EC笔记:第二部分:12、复制对象时勿忘其每一个成分

    EC笔记:第二部分:12.复制对象时勿忘其每一个成分 1.场景 某些时候,我们不想使用编译器提供的默认拷贝函数(包括拷贝构造函数和赋值运算符),考虑以下类定义: 代码1: class Point{ p ...

  9. EC读书笔记系列之7:条款12 复制对象时勿忘其每一个成分

    记住: ★copying函数应确保复制“对象内的所有成员变量”及“所有base class成分” ★不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个函数中,并由两 ...

随机推荐

  1. 手对手的教你用canvas画一个简单的海报

    啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信.qq等平台.图片中包含用户的姓名.头像.和带着自己信息的二维码.然后,如何生成这张海报呢~~~首先我们老大告诉我有一个插件叫htm ...

  2. 树(二叉树 & 二叉搜索树 & 哈夫曼树 & 字典树)

    树:n(n>=0)个节点的有限集.有且只有一个root,子树的个数没有限制但互不相交.结点拥有的子树个数就是该结点的度(Degree).度为0的是叶结点,除根结点和叶结点,其他的是内部结点.结点 ...

  3. Python实现的数据结构与算法之基本搜索详解

    一.顺序搜索 顺序搜索 是最简单直观的搜索方法:从列表开头到末尾,逐个比较待搜索项与列表中的项,直到找到目标项(搜索成功)或者 超出搜索范围 (搜索失败). 根据列表中的项是否按顺序排列,可以将列表分 ...

  4. interp1一维数据插值在matlab中的用法

    转载:https://ww2.mathworks.cn/help/matlab/ref/interp1.html?s_tid=srchtitle#btwp6lt-2_1 interp1 一维数据插值( ...

  5. matlab中nargin函数输入参数数目

    来源:https://ww2.mathworks.cn/help/matlab/ref/nargin.html?searchHighlight=nargin&s_tid=doc_srchtit ...

  6. selenium3+python3自动化环境搭建

    (我也是小白,刚开始接触自动化,以下内容是我自己在配置环境的时候遇到的问题及解决方法,是后面才记录的要是有什么遗漏或者问题,欢迎帮忙指出来.)1.1首先下载python下载网址:https://www ...

  7. Linux网卡命名规则

    网卡命名 一.为什么需要这个      服务器通常有多块网卡,有板载集成的,同时也有插在PCIe插槽的.Linux系统的命名原来是eth0,eth1这样的形式,但是这个编号往往不一定准确对应网卡接口的 ...

  8. linux 中 eclipse 开发 c/c++ 多线程程序,添加 libpthread.a 库支持

    导入头文件 在 linux 中开发多线程程序,在使用到 pthread 系列函数的文件中,需要导入头文件: #include <pthread.h> 链接 libpthread.a 在编译 ...

  9. 一文看懂Vue3.0的优化

    1.源码优化: a.使用monorepo来管理源码 Vue.js 2.x 的源码托管在 src 目录,然后依据功能拆分出了 compiler(模板编译的相关代码).core(与平台无关的通用运行时代码 ...

  10. 【水】怎么在 HZOI 上水到更高的分

    前言 这些东西在联赛并用不了 预编译优化 40行优化 #define _CRT_SECURE_NO_WARNINGS #pragma GCC optimize(2) #pragma GCC optim ...