原题地址:https://www.luogu.org/problem/P5072

题目简述

给定一个序列,每次查询一个区间[l,r]中所有子序列分别去重后的和mod p


思路

我们考虑每个数的贡献。即该区间内含有这个数的子序列个数。用补集转化为不含这个数的子序列个数。

那么,假设这个数在[l,r]内出现了kk次,则一共有2(r-l+1)-2(r-l+1-k)个子序列包含这个数。

本题可以离线,因此选择使用莫队,过程中维护cnt[k]表示区间内恰好出现k次的数字个数,维护sum[j]表示区间内恰好出现j次的数字之和(区间内出现次数相同的数,对于这些数,区间中包含这些数的子序列个数都相同,因此存数字之和就行)。

然而这样时间复杂度为O(询问次数*单次询问复杂度)=O(n*max(sqrt(n),n))=O(nm),并不可行。我们发现时间瓶颈不在莫队的sqrt(n),而是在单次查询中求解的复杂度n。

有2个套路可供使用:出现次数大于sqrt(n)的数不超过sqrt(n)个,值不为0的cnt[k]少于2*sqrt(n)个(反证易得,本质类似)。

  • 对于第一个套路,我们分类讨论:出现次数小于等于sqrt(n),则统计每个出现次数的数字之和;大于sqrt(n)的用哈希表(unordered_set,C++11)存下具体的数字和其出现次数。这样每次查询是sqrt(n)。
  • 笔者使用的则是第二个套路:val[x]表示出现次数恰好为x的数字之和(同上文的sum[j])。随着莫队l,r指针的移动,把所有可能变为非0的val[x]记下来,指针移动完毕后再对其进行筛选,把确实非0的val[x]保留,其他去除。这样计算单次答案的复杂度就等同于单次查询中莫队指针移动的平均步数:都是sqrt(n)级别。这样做不需要用到哈希表之类的,常数小了很多,甚至不需要读入优化也能轻松过。

还没完。我们发现模数是不定的,为了保证单次查询的复杂度压在sqrt(n)以内,我们还有最后一件事情要做:在sqrt(n)的时间内求出2(r-l+1)和所有的2(r-l+1-k)。这里安利一个神奇的方式:每次查询只需要做一次时间复杂度为sqrt(n)的预处理就可以O(1)查询了。

假设查询区间长度为len(len=r-l+1),我们记siz=sqrt(len),而后计算20,21,22...2sqrt(len),存在数组pow1中;再计算2sqrt(len),2(2*sqrt(len)),2(3*sqrt(len)),2(4*sqrt(len))...,2^(sqrt(len)*sqrt(len)),存在数组pow[2]中。以上计算都在mod p意义下进行。

这样求2的任意次方都可以O(1)出解:2k=2(k/siz)*2^(k%siz)=pow2[k/siz]*pow1[k%siz](记得模p)。


代码

#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
int n,m;
int a[MAXN],val[MAXN*2],cnt[MAXN],ans[MAXN];
int tot,blosiz,powsiz;
int bel[MAXN],pow1[MAXN],pow2[MAXN];
bool calced[MAXN]; long long sum[MAXN];
struct query {
int id,l,r,p;
bool operator<(const query &sb) const {
return bel[l]!=bel[sb.l] ? bel[l]<bel[sb.l] : (bel[l]&1 ? r<sb.r : r>sb.r);
}
} q[MAXN]; void add(int x)
{
sum[cnt[x]]-=x;
cnt[x]++;
sum[cnt[x]]+=x;
++tot;
val[tot]=cnt[x];
}
void del(int x)
{
sum[cnt[x]]-=x;
cnt[x]--;
sum[cnt[x]]+=x;
++tot;
val[tot]=cnt[x];
}
int power(int x,int p)
{
return 1LL*pow1[x%powsiz]*pow2[x/powsiz]%p;
}
int main()
{
scanf("%d%d",&n,&m);
blosiz=sqrt(n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),bel[i]=(i-1)/blosiz;
for (int i=1;i<=m;i++)
scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].p),q[i].id=i;
sort(q+1,q+m+1);
int l=1,r=0;
for (int i=1;i<=m;i++) {
while (l>q[i].l) add(a[--l]);
while (r<q[i].r) add(a[++r]);
while (l<q[i].l) del(a[l++]);
while (r>q[i].r) del(a[r--]); int newtot=0;
int len=r-l+1;
for (int j=1;j<=tot;j++)
if (val[j]&&sum[val[j]]!=0&&!calced[val[j]])
calced[val[j]]=1,val[++newtot]=val[j];
tot=newtot; powsiz=sqrt(len)+1;
pow1[0]=pow2[0]=1;
for (int j=1;j<=powsiz;j++)
pow1[j]=(pow1[j-1]+pow1[j-1])%q[i].p;
for (int j=1;j*powsiz<=len;j++)
pow2[j]=1LL*pow2[j-1]*pow1[powsiz]%q[i].p; int powLen=power(len,q[i].p);
for (int j=1;j<=tot;j++) {
long long num=sum[val[j]]%q[i].p;
ans[q[i].id]=(ans[q[i].id]+num*(powLen-power(len-val[j],q[i].p)))%q[i].p;
calced[val[j]]=0;
}
ans[q[i].id]+=q[i].p;
ans[q[i].id]%=q[i].p;
}
for (int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}

洛谷:P5072 [Ynoi2015]盼君勿忘的更多相关文章

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

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

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

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

  3. P5072 [Ynoi2015]盼君勿忘

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

  4. Luogu P5072 [Ynoi2015]盼君勿忘

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

  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. 洛谷P5069 [Ynoi2015]纵使日薄西山(树状数组,set)

    洛谷题目传送门 一血祭 向dllxl致敬! 算是YNOI中比较清新的吧,毕竟代码只有1.25k. 首先我们对着题意模拟,寻找一些思路. 每次选了一个最大的数后,它和它周围两个数都要减一.这样无论如何, ...

  9. 洛谷 P5071 - [Ynoi2015] 此时此刻的光辉(莫队)

    洛谷题面传送门 一道其实算得上常规的题,写这篇题解是为了总结一些数论中轻微(?)优化复杂度的技巧. 首先感性理解可以发现该问题强于区间数颜色问题,无法用常用的 log 数据结构维护,因此考虑分块/莫队 ...

随机推荐

  1. NLP(十三) 词义消歧

    一个词可能有多个词义 例句 解释 She is my date date: 约会,日期 You have taken too many leaves to skip cleaning leaves i ...

  2. lightoj 1158 - Anagram Division(记忆化搜索+状压)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1158 题解:这题看起来就像是记忆搜索,由于s很少最多就10位所以可以考虑用状压 ...

  3. 2018湖南多校第二场-20180407 Barareh on Fire

    Description The Barareh village is on fire due to the attack of the virtual enemy. Several places ar ...

  4. react页面内嵌微信二维码 和 自定义样式 以及 微信网页共用unionId问题

    在react页面内嵌“微信二维码”,实现PC端通过微信扫码进行登录.首先去微信开放平台注册一个账号,创建一个网站应用,提交网站备案审核,获取appid和appsecret:其他开发流程根据微信文档来进 ...

  5. 【Offer】[61] 【扑克牌中的顺子】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的.2~10 为数字本身,A为1, J为11, Q为12,K为13,而大 ...

  6. 封装axios来管控api的2种方式

    前言:我们在开发项目的时候,往往要处理大量的接口.并且在测试环境 开发环境 生产环境使用的接口baseurl都不一样 这时候如果在开发环境完成之后切换每一个接口的baseurl会变的非常的麻烦,(要去 ...

  7. this指哪儿

    this的指向问题 一.this初识 this是javascript中最复杂的机制之一.它尤为特殊,被自动定义在所有函数的作用域中.这篇文章将浅析this与函数的关系. 二.了解this 学习this ...

  8. 聊聊JavaScript异步中的macrotask和microtask

    前言 首先来看一个JavaScript的代码片段: console.log(1); setTimeout(() => { console.log(2); Promise.resolve().th ...

  9. Android开发教程:开发框架基本原理

    1.提供应用程序框架(Framework) 开发者可以遵照这些框架搭建应用程序读者可以结合J2SE平台的Applet框架或J2ME平台的移动信息设备套件框架来理解Android平台的应用程序框架. 每 ...

  10. ES6-数组的新方法

    1.Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型. Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7)创建一个 ...