题意

给定一个长度为 \(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. MySQL手注之联合查询注入

    了解联合查询注入之前,先要了解一下什么是union? union是用于合并两个sql查询结果的语句. 要使用union 必须有相同的列数  必须有两条以上的select语句组成  列的数据类型必须兼容 ...

  2. WPF DataGrid 复合表头 (实现表头合并,自定义表头)

    功能说明: 将 DataGrid嵌套在本控件内,使用Label自定义表头,如果需要上下左右滚动 需要在控件外围添加  ScrollViewer 并且设置  ScrollVisibility 为Auto ...

  3. python下的appium控制andriod按键

    今天查看视频的时候,发现不好控制关闭视频,于是就想控制手机的返回按键来达到返回的效果. 找到了一些关于按键的keycode,如下: KEYCODE_CALL 拨号键 5 KEYCODE_ENDCALL ...

  4. Python字符编码和二进制不得不说的故事

    二进制 核心思想: 冯诺依曼 + 图灵机 电如何表示状态,才能稳定? 计算机开始设计的时候并不是考虑简单,而是考虑能自动完成任务与结果的可靠性, 简单始终是建立再稳定.可靠基础上 经过尝试10进制,但 ...

  5. Power Designer建模之餐饮在线点评系统——需求模型实例

    总览 一级需求 二级需求 管理门户  会员门户 三级需求 系统管理  企业中心  会员中心  统计分析 四级需求

  6. 016 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 10 布尔类型和字符串的字面值

    016 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 10 布尔类型和字符串的字面值 本文知识点:字面值 关于字面值的概念,需要注意:很多地方,我们可能就把字面值 ...

  7. Java知识系统回顾整理01基础04操作符05赋值操作符

    一.赋值操作 赋值操作的操作顺序是从右到左 int i = 5+5; 首先进行5+5的运算,得到结果10,然后把10这个值,赋给i public class HelloWorld { public s ...

  8. 【随笔】菜刀(代码执行)函数和命令执行函数详解及Getshell方法

    代码执行函数 VS 命令执行函数 一直想整理这两块的内容,但是一直没时间弄,直到前两天碰上一个写入了菜刀马但是死活连不上菜刀的站,顿时不知道怎么继续了,所以就趁这个机会整理了一下代码执行函数怎么get ...

  9. farbic-sdk-java 学习部署

    准备工作 1.fabric基础网络环境 2.环境准备(jdk环境.maven环境) 3.启动fabric测试网络 4.在idea中测试java-sdk 1.fabric环境准备 1.fabric基础环 ...

  10. 用vscode写python,from引用本地文件的时候老是有红色波浪线,很不爽

    前言 出于一些原因,国际关系等等,最近想把开发工具切换到一些免费开源的工具上面,先尝试了在vscode上搭建python环境,总体还是很简单的, 网上教程很多,vscode本身的插件也很丰富,可惜了国 ...