P4135 作诗
分块
设sum[ i ] [ j ] 存从左边到第 i 块时,数字 j 的出现次数
f [ i ] [ j ] 存从第 i 块,到第 j 块的一整段的答案
那么最后答案就是一段区间中几块整段的答案加上两边小段的贡献
考虑两边小段的影响,对于每一个出现的数
它可能会使答案增加(使原本大区间中出现奇数次的数变成出现偶数次)
也可能使答案减少(使原本大区间中出现偶数次的数变成出现奇数次)
有了 sum 数组我们可以很方便地求出大区间中每个数的出现次数
对小段的每个数直接计算一下它对答案的贡献
开一个 cnt[ i ] 记录一下之前每个数 i 出现的次数就好了
//bl,br是大区间的左右边界的块
ans=f[bl][br];//ans初值为大区间的答案
for(int i=l;i<L[bl];i++)//L[i]存第i块的左端点
{
cnt[a[i]]++;//记录a[i]在小段出现的次数
t=cnt[a[i]]+sum[br][a[i]]-sum[bl-/*注意是bl-1*/][a[i]];//计算此时a[i]出现的次数
if(!(t&)) ans++;//如果a[i]出现的次数变成了偶数次,答案就加一
else if(t>) ans--;//否则如果出现次数超过2次且使出现次数变成奇数次,答案减1
//要特殊考虑t=1的情况,t=1时不会对答案有影响,因为t=0时不算出现偶数次
}
for(int i=L[br+];i<=r;i++)//注意i的范围
{
cnt[a[i]]++; t=cnt[a[i]]+sum[br][a[i]]-sum[bl-][a[i]];
if(!(t&)) ans++;
else if(t>) ans--;
}
for(int i=l;i<L[bl];i++) cnt[a[i]]--; for(int i=L[br+];i<=r;i++) cnt[a[i]]--;//别忘记还原cnt,以后询问还要用,注意不要用memset
关于询问
然后来考虑如何预处理
sum数组很好求,关键是 f
好像枚举每个块复杂度会爆炸...
我们来看看处理询问时的方法,对每个数都计算贡献
预处理就相当于询问每两个块之间的贡献
我们可以用同样的方法,cnt[ i ] 记录 i 出现了几次
从左到右扫,计算每个数对答案的贡献,如果扫到一个区间的右端点了,就记录一波 f
//bel[i]表示点i属于第几个块
for(int i=;i<=bel[n];i++)//枚举每个左块
{
t=;
for(int j=L[i];j<=n;j++)//考虑右边所有块
{
cnt[a[j]]++;//记录
if(!(cnt[a[j]]&)) t++;
else if(cnt[a[j]]>) t--;
//考虑对答案的贡献
if(bel[j]!=bel[j+]) f[i][bel[j]]=t;//如果到了一个区间的右端点,更新f
}
for(int j=L[i];j<=n;j++) cnt[a[j]]--;//别忘了还原cnt
}
关于预处理
这样我们就可以在 O( n*sqrt(n) )的时间内预处理,在 O( sqrt(n)+2*sqrt(n) ) 的时间处理询问
注意一下常数就轻松过了
细节很多的一题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'')
{
if(ch=='-') f=-;
ch=getchar();
}
while(ch>=''&&ch<='')
{
x=(x<<)+(x<<)+(ch^);
ch=getchar();
}
return x*f;
}
const int N=1e5+,M=;
int n,m,q,ans;
int a[N],bel[N],L[M];
int sum[M][N],f[M][M]; int cnt[N];
inline void pre()//预处理
{
int t=;
for(int i=;i<=bel[n];i++)
for(int j=;j<=m;j++) sum[i][j]+=sum[i-][j];
for(int i=;i<=bel[n];i++)
{
t=;
for(int j=L[i];j<=n;j++)
{
cnt[a[j]]++;
if(!(cnt[a[j]]&)) t++;
else if(cnt[a[j]]>) t--;
if(bel[j]!=bel[j+]) f[i][bel[j]]=t;
}
for(int j=L[i];j<=n;j++) cnt[a[j]]--;
}
}
inline void query(int l,int r)//处理询问
{
ans=; int t=;
if(bel[l]+>=bel[r])//对没有大区间的情况特殊处理
{
for(int i=l;i<=r;i++)//直接爆力计算每个数的贡献
{
cnt[a[i]]++;
if(!(cnt[a[i]]&)) ans++;
else if(cnt[a[i]]>) ans--;
}
for(int i=l;i<=r;i++) cnt[a[i]]--;//清空cnt
return;
}
int bl=bel[l-]+,br=bel[r+]-;//计算bl,br,细节
//l-1是考虑当l在一个块最左边的时候,那l在的块整块都会每计算,直接整个拿出来计算就好了,r+1同理
ans=f[bl][br];//初值
for(int i=l;i<L[bl];i++)//对左边的小段计算贡献
{
cnt[a[i]]++; t=cnt[a[i]]+sum[br][a[i]]-sum[bl-][a[i]];
if(!(t&)) ans++;
else if(t>) ans--;
}
for(int i=L[br+];i<=r;i++)//对右边的小段计算贡献
{
cnt[a[i]]++; t=cnt[a[i]]+sum[br][a[i]]-sum[bl-][a[i]];
if(!(t&)) ans++;
else if(t>) ans--;
}
for(int i=l;i<L[bl];i++) cnt[a[i]]--; for(int i=L[br+];i<=r;i++) cnt[a[i]]--;//清空
return;
}
int main()
{
n=read(); m=read(); q=read();
int t=sqrt(n)+;
for(int i=;i<=n;i++)
{
a[i]=read(); bel[i]=(i-)/t+;//处理bel
if(bel[i]!=bel[i-]) L[bel[i]]=i;//处理L
sum[bel[i]][a[i]]++;//此时sum只包括第i块的数量
}
bel[n+]=bel[n]+; L[bel[n+]]=n+;//重要的细节,有时我的代码考虑边界时会访问到n+1的点 pre(); int l,r;
for(int i=;i<=q;i++)
{
l=read(); r=read();
l=(l+ans)%n+; r=(r+ans)%n+;
if(l>r) swap(l,r);//处理l,r
query(l,r);
printf("%d\n",ans);
}
return ;
}
P4135 作诗的更多相关文章
- 洛谷P4135 作诗 (分块)
洛谷P4135 作诗 题目描述 神犇SJY虐完HEOI之后给傻×LYD出了一题: SHY是T国的公主,平时的一大爱好是作诗. 由于时间紧迫,SHY作完诗之后还要虐OI,于是SHY找来一篇长度为N的文章 ...
- P4135 作诗——分块
题目:https://www.luogu.org/problemnew/show/P4135 分块大法: 块之间记录答案,每一块记录次数前缀和: 注意每次把桶中需要用到位置赋值就好了: 为什么加了特判 ...
- 洛谷P4135 作诗
题意:[l,r]之间有多少个数出现了正偶数次.强制在线. 解:第一眼想到莫队,然后发现强制在线...分块吧. 有个很朴素的想法就是蒲公英那题的套路,做每块前缀和的桶. 然后发现这题空间128M,数组大 ...
- luogu P4135 作诗
嘟嘟嘟 郑重声明:我的前几到分块题写法上都有点小毛病,以这篇为主! 这道题感觉也是分块的基本套路,只不过卡常,得开氧气. 维护俩:sum[i][j]表示前 i 块中,数字 j 出现了多少次,ans[i ...
- 洛谷 P4135 作诗
分块大暴力,跟区间众数基本一样 #pragma GCC optimize(3) #include<cstdio> #include<algorithm> #include< ...
- 【分块】P4135 作诗
分块太暴力惹... 没做出来.看了题解qaq 分析: 两头$\sqrt{n}$暴力维护 预处理ans[i][j],sum[i][j] sum[i][j]是一个前缀和,前i块值为j的数量 ans[i][ ...
- 洛谷 P4135 作诗(分块)
题目链接 题意:\(n\) 个数,每个数都在 \([1,c]\) 中,\(m\) 次询问,每次问在 \([l,r]\) 中有多少个数出现偶数次.强制在线. \(1 \leq n,m,c \leq 10 ...
- 洛谷P4135 作诗(不一样的分块)
题面 给定一个长度为 n n n 的整数序列 A A A ,序列中每个数在 [ 1 , c ] [1,c] [1,c] 范围内.有 m m m 次询问,每次询问查询一个区间 [ l , r ] [l, ...
- 洛谷 P4135 作诗 题解
题面. 之前做过一道很类似的题目 洛谷P4168蒲公英 ,然后看到这题很快就想到了解法,做完这题可以对比一下,真的很像. 题目要求区间内出现次数为正偶数的数字的数量. 数据范围1e5,可以分块. 我们 ...
随机推荐
- [HDU4652]Dice
vjudge 题意 \(m\)面骰子,求 1.连续出现\(n\)个相同的停止: 2.连续出现\(n\)个不同的停止 的期望投骰子次数. \(n,m ≤ 10^6\) sol 首先考虑一个转移式子吧. ...
- 2017.10.5北京清北综合强化班DAY5
拼不出的数lost.in/.out/.cpp[问题描述]3 个元素的集合{5, 1,2} 的所有子集的和分别是0,1, 2, 3, 5, 6, 7, 8.发现最小的不能由该集合子集拼出的数字是4.现在 ...
- linux绑定多个ip(转载)
在Linux下有时候需要给一个网卡绑定多个IP,本文介绍在Redhat系列(redhat,Fedora Core,Centos)中的实现方法和一种在Gentoo等其他Linux中普遍适用的方法. 1. ...
- tx2在自制载板上无法识别usb以及pcie无法读取数据
注意使用的系统版本是Jetpack-3.1,其它版本的系统上没有测试过!!! 刷机时替换dtb文件: 将Jetpack刷机包 64_TX2/Linux_for_Tegra_tx2/kernel/dtb ...
- 【P2P网贷新手入门】详解借款标的种类及其风险
不同于国外的网贷平台以信用借款标为主,在中国,我们投资网贷平台会看到多样借款标,而投资人往往弄不清自己投资的标属于什么类型的标,特点怎么样,风险如何. 抵 押 标 定义:借款人用自己的房屋车辆等实物在 ...
- 输出缓存与CachePanel
缓存的级别 缓存的作用自不必说,提高系统性能最重要的手段之一.上至应用框架,下至文件系统乃至CPU,计算机中各部分设计都能见到缓存的身影.许多朋友一直在追求如何提高Web应用程序的性能,其实最容易被理 ...
- ASP.NET MVC 3:缓存功能的设计问题
今天这一篇文章我来谈一谈在MVC 3项目中的缓存功能,以及针对缓存的一些设计上的考量,给大家参考参考. 为什么需要讨论缓存?缓存是一个中大型系统所必须考虑的问题.为了避免每次请求都去访问后台的资源(例 ...
- <正则吃饺子> :关于使用pd创建表时需要注意的地方
公司项目使用pd设计数据库表.之前用过,但是年代比较久远了,有些细节忘记了,今天重新使用时候,生疏了,现在稍微记录下吧. 1.pd创建表的使用,可以直接从网上搜索,博文比较多,如 “pd 设计数据库表 ...
- 使用gradle上传项目到jcenter
想不想把自己的库也上传到jcenter,然后只需要一名话 compile com.zzb.library:android-common:0.1.0 //(compile group_id:artifa ...
- mysql四个默认数据库
1.Master数据库 Master数据库记录了Sqlserver所有的服务器级系统信息,所有的注册帐户和密码,以及所有的系统设置信息,还记录了所有用户定义数据库的存储位置和初始化信息. 2.Tem ...