P3604 美好的每一天

题目描述

给定一个字符串,提供m个访问区间,求每次访问中,访问区间内满足区间内字符重排可形成回文序列的子区间个数。

输入格式

第一行输入两个数据,分别为n、m;第二行输入长为n的仅由小写字母构成的字符串;接下来输入m组查询数据,每组数据包含两个数据,分别是访问区间的左端点和右端点。

输出格式

输出m行数据,每一行为对应查询区间满足条件的子区间个数。

输入样例#1

6 6
zzqzzq
1 6
2 4
3 4
2 3
4 5
1 1

输出样例#1

16
4
2
2
3
1

解题思路

  一个区间内的字符串经过重排可以构成回文序列的充要条件是:所有字母均有偶数个或者仅有一个字母为奇数个其余均有偶数个。因此,可以使用一个26位的二进制数来表示每个字母的奇偶状态,该二进制数从低位到高位分别表示a-z的奇偶性,0表示有偶数个,1表示有奇数个。当为回文序列时,该二进制数必满足为0为2^k
  当给定一个访问区间时,不但需要判断这个区间是否满足重排形成回文,还需要判断子区间是否满足重排形成回文。因此如果直接遍历给定区间的所有子区间并计算子区间各字母奇偶状态,必定导致复杂度上升。这时我们考虑到莫队的left和right区间指针移动的特性,由于每次只能移动一个单位,因此指针移动的过程中就完成了对所有子区间的遍历,这样便将复杂度降低到了O(nn)O(n\sqrt n)O(nn​)。
  莫队的指针移动是如何完成对子区间的遍历的呢?当指定访问区间由[left,tight][left,tight][left,tight]变为[left,right+1][left,right+1][left,right+1]时,由于[left,tight][left,tight][left,tight]区间内的子区间情况已经遍历过,实际上只需判断新添加的right+1right+1right+1位置的数据与[left,tight][left,tight][left,tight]中各个点组合成的子区间是否满足条件。
  下考虑如何快速得到各区间的字母奇偶状态。使用sum[i]sum[i]sum[i]来存储区间[1,i][1,i][1,i]之间所有字母的奇偶状态;因此想要获得区间[x,y][x,y][x,y]的字母奇偶状态,只需要计算sum[y]xorsum[x−1]sum[y]\ xor\ sum[x-1]sum[y] xor sum[x−1]即可。因此在输入字符串的过程中,即可直接计算出sum[]sum[]sum[]数组的值,具体如下:

for(register int i = 1; i <= n; i++){sum[i] = sum[i - 1] ^ (1 << (getchar() - 'a'));
}

  如何记录操作区间内各状态的个数?我们使用cnt[i]cnt[i]cnt[i]数组来存储莫队left指针和right指针指向区间内i状态下的区间个数。举例:对于字符串zppp来说,区间[1,2][1,2][1,2]的状态和区间[1,4][1,4][1,4]的状态相同,因此满足该状态的个数cnt[(1<<25)+(1<<15)]=2cnt[(1<<25)+(1<<15)]=2cnt[(1<<25)+(1<<15)]=2。而由于总共有26个字母,每种字母对应2种状态——奇数个或偶数个,因此总共可能的状态有2262^{26}226个,即cnt[]cnt[]cnt[]数组的大小应大于等于2262^{26}226。
  如何在区间扩充或缩减时更新子区间个数?设xxx为区间[left,right][left,right][left,right]内某一点,当区间由[left,right][left,right][left,right]扩充为[left,right+1][left,right+1][left,right+1]时,如果区间[x,right+1][x,right+1][x,right+1]内各字母状态对应的二进制数a必满足为0或为2k2^k2k。由于cnt[a]cnt[a]cnt[a]记录的是区间[left,right][left,right][left,right]内满足状态a的子区间个数,因此若sum[right+1]sum[right+1]sum[right+1]的状态也为a,则意味着区间[1,left−1][1,left-1][1,left−1]之间的字母状态没有影响,且[x+1,right][x+1,right][x+1,right]区间内的字母状态也没有影响,即[x,right+1][x,right+1][x,right+1]区间字母状态满足异或和为0,为回文序列。而区间[left,right][left,right][left,right]内所有满足与right+1异或为0的点的个数为cnt[sum[right+1]]cnt[sum[right+1]]cnt[sum[right+1]]。换句话说cnt[sum[right+1]]cnt[sum[right+1]]cnt[sum[right+1]]记录了区间内所有满足sum[x−1]xorsum[right+1]==0sum[x-1]\ xor\ sum[right+1]==0sum[x−1] xor sum[right+1]==0的子区间的个数。同理我们可以得出满足为2k2^{k}2k形式的区间必满足sum[x−1]xorsum[right+1]==2ksum[x-1]\ xor\ sum[right+1]==2^ksum[x−1] xor sum[right+1]==2k,其中0≤k≤250\leq k\leq250≤k≤25,即满足sum[x−1]xorsum[right+1]==1<<ksum[x-1]\ xor\ sum[right+1]==1<<ksum[x−1] xor sum[right+1]==1<<k,即满足sum[x−1]xorsum[right+1]xor(1<<k)==0sum[x-1]\ xor\ sum[right+1]\ xor\ (1<<k)==0sum[x−1] xor sum[right+1] xor (1<<k)==0,即区间个数由cnt[sum[right+1]xor(1<<k)]cnt[sum[right+1]\ xor\ (1<<k)]cnt[sum[right+1] xor (1<<k)]记录。据此构建莫队关键的更新函数如下:

inline void add(register int pos){res += cnt[sum[pos]];  //res加上区间内满足sum[j]^sum[pos]==0的个数cnt[sum[pos]]++;for(register int i = 0; i < 26; i++) res += cnt[sum[pos] ^ (1 << i)];  //加上区间内满足sum[j]^sum[pos]==2^k的个数
}inline void del(register int pos){cnt[sum[pos]]--;res -= cnt[sum[pos]];for(register int i = 0; i < 26; i++) res -= cnt[sum[pos] ^ (1 << i)];
}

  随后直接利用莫队对查询区间进行排序后遍历即可,如下:

int left = 1, right = 0;  //左右端点指针
for(register int i = 0; i < m; i++){register int l = visits[i].left - 1, r = visits[i].right;  //求区间[left,right]需要计算的是sum[left-1]^sum[right],因此l赋值时需减1while(left < l) del(left++);while(left > l) add(--left);while(right < r) add(++right);while(right > r) del(right--);out[visits[i].id] = res;
}

  由于该题数据量较大,因此使用快读。快读在计算数据值时也存在技巧。当为字符'0'-'9'之间时,需要进行ans = ans * 10 + ch - '0'的计算,但考虑到乘法运算较慢,又因为10=8+2=23+210=8+2=2^3+210=8+2=23+2,因此将ans * 10改写为位运算(ans << 3) + (ans << 1)ch - '0'由于字符'0'ASCII码的特殊性,因此可改写为ch ^ 48;综上可改为ans = (ans << 3) + (ans << 1) + (ch ^ 48)。快读函数如下:

inline int read(){ //快读register char ch = getchar();while(ch < '0' || ch > '9') ch = getchar();register int ans = 0;while(ch >= '0' && ch <= '9'){ans = (ans << 3) + (ans << 1) + (ch ^ 48);ch = getchar();} return ans;
}

  由于该题卡常苛刻,因此使用sort()sort()sort()函数时采用一般的莫队排序方法会遭遇卡常,此时采用分块排序的方法,将块按照basebasebase大小分,在basebasebase区间内的直接按rightrightright从小到大排序;在basebasebase区间外的按leftleftleft从小到大排序。同时由于构造sort()sort()sort()函数所需的cmp()cmp()cmp()函数,会导致访问时间的增加,因此直接在结构体声明中重定义<即可。如下:

struct visit{int id, left, right;bool operator < (visit vst)const{return left / base == vst.left / base ? right < vst.right : left < vst.left;}
}visits[MAX];base = 3 * sqrt(n);
sort(visits, visits + m);

代码

  由于该题卡常严重,使用#pragma GCC optimize(2)仍会卡两个测试点,因此不需要手动代码添加编译优化,而直接点击O2优化即可AC。

#include <stdio.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define MAX 60005int n, m, base;
int sum[MAX], cnt[(1 << 26) + 5], out[MAX];
//sum是前缀异或和;由于有26个字母,每个字母有奇偶两种状态,因此cnt用来存储当前区间内某种状态下对应的区间个数
struct visit{int id, left, right;bool operator < (visit vst)const{return left / base == vst.left / base ? right < vst.right : left < vst.left;}
}visits[MAX];
int res = 0;inline int read(){ //快读register char ch = getchar();while(ch < '0' || ch > '9') ch = getchar();register int ans = 0;while(ch >= '0' && ch <= '9'){ans = (ans << 3) + (ans << 1) + (ch ^ 48);ch = getchar();} return ans;
}inline void add(register int pos){res += cnt[sum[pos]];  //res加上区间内与当前状态一致的区间个数,即区间内满足sum[j]^sum[pos]==0的个数cnt[sum[pos]]++;for(register int i = 0; i < 26; i++) res += cnt[sum[pos] ^ (1 << i)];  //加上区间内满足sum[j]^sum[pos]==2^k的个数
}inline void del(register int pos){cnt[sum[pos]]--;res -= cnt[sum[pos]];for(register int i = 0; i < 26; i++) res -= cnt[sum[pos] ^ (1 << i)];
}int main(){//输入 n = read(), m = read();base = 3 * sqrt(n);register char ch = getchar();while(ch < 'a' || ch > 'z') ch = getchar();sum[1] = 1 << (ch - 'a');for(register int i = 2; i <= n; i++){sum[i] = sum[i - 1] ^ (1 << (getchar() - 'a'));}for(register int i = 0; i < m; i++){visits[i].id = i;visits[i].left = read();visits[i].right = read();}sort(visits, visits + m);  //莫队排序int left = 1, right = 0;for(register int i = 0; i < m; i++){register int l = visits[i].left - 1, r = visits[i].right;while(left < l) del(left++);while(left > l) add(--left);while(right < r) add(++right);while(right > r) del(right--);out[visits[i].id] = res;}    for(register int i = 0; i < m; i++) printf("%d\n", out[i]);return 0;
}

结果

洛谷 P3604 美好的每一天相关推荐

  1. B 洛谷 P3604 美好的每一天 [莫队算法]

    题目背景 时间限制3s,空间限制162MB 素晴らしき日々 我们的情人,不过是随便借个名字,用幻想吹出来的肥皂泡,把信拿去吧,你可以使假戏成真.我本来是无病呻吟,漫无目的的吐露爱情---现在这些漂泊不 ...

  2. DP【洛谷P2134】 百日旅行

    [洛谷P2134] 百日旅行 题目背景 重要的不是去哪里,而是和你在一起.--小红 对小明和小红来说,2014年7月29日是一个美好的日子.这一天是他们相识100天的纪念日. (小明:小红,感谢你2场 ...

  3. [洛谷]CON1466 洛谷2017春节联欢赛 Hello Dingyou题解 Bzoj4763雪辉

    题目来源:https://www.luogu.org/contest/show?tid=1466 创建时间:2017/3/13 18:33 镇楼图:       猜猜她是谁~ 解题思路: 春节居然也有 ...

  4. 我的洛谷冬日绘板计划

    我为什么要画这个? 首先,这是一只金色的企鹅--不是鸭子啦! 金企鹅(JQE)是我的一个学长,从我走上oi这条路开始,他就一直在给予我极大的帮助. 可是oi的美好时光流逝得太快,现在他已经高三了,成为 ...

  5. 洛谷 T284709 怨念(resent)

    PS:如果读过题了可以跳过题目描述直接到题解部分 提交链接:洛谷 T284709 怨念(resent) 题目 题目背景 "结束了."dlh眼睁睁地看着右下角的时间到达13:00.屏 ...

  6. 【LGR-142-Div.4】洛谷入门赛 #13 考后分析与题解

    洛谷入门赛 #Round 13 比赛分析与总结 T1 魔方 题目背景 题目描述 输入格式 输出格式 样例 #1 样例输入 #1 样例输出 #1 提示 数据规模与约定 分析 AC代码 注意 T2 教学楼 ...

  7. 洛谷-题解 P2672 【推销员】

    独门思路!链表加优先队列! 这题一望,贪心是跑不掉了,但是我贪心并不好,所以想到了一个复杂一些但思路更保稳的做法 思路: 1 因为是离线操作,所以我们可以倒着求,先求x=n的情况,因为那样直接就知道了 ...

  8. 洛谷 P1142 轰炸

    洛谷 P1142 轰炸 题目描述 "我该怎么办?"飞行员klux向你求助. 事实上,klux面对的是一个很简单的问题,但是他实在太菜了. klux要想轰炸某个区域内的一些地方,它们 ...

  9. 洛谷 P1387 最大正方形

    P1387 最大正方形 题目描述 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入输出格式 输入格式: 输入文件第一行为两个整数n,m(1<=n,m<=10 ...

  10. 洛谷P2763 试题库问题

    题目:https://www.luogu.org/problemnew/show/P2763 题目描述 «问题描述: 假设一个试题库中有n道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性. ...

最新文章

  1. android dump 完整so,Android dump .so 文件crash log
  2. Spark的RDD 文件读取与保存
  3. 盘点大数据生态圈,那些繁花似锦的开源项目
  4. 第30天:项目时间管理相关错题整理
  5. 王高利:Apache Httpd负载均衡Tomcat并实现Session Sticky和Session Cluster
  6. 创新设计模式:抽象工厂模式
  7. ubuntu display
  8. Java中父类强制转换为子类的可能
  9. 数字金额转为大写金额(C#)
  10. 【April Fools Day Contest 2014F】【愚人节脑洞 OEIS大法】000001 输出序列某一项
  11. matlab激活中遇到 matlab2017a license error -8,523错误
  12. Lodop6.226_Clodop3.075.zip
  13. mac安装win7流程 win7提示无法验证数字签名的解决
  14. OSChina 周六乱弹 —— 我都想和他们组成一个家庭了
  15. pdf文件转换成word文本文档去水印怎么弄
  16. R语言使用cph函数和rcs函数构建限制性立方样条cox回归模型、检验模型是否满足等比例风险、是否存在非线性关系、使用rms包的Predict函数计算指定连续变量和风险比HR值的关系并可视化
  17. Java基于Redis实现附近的人(内附源码)
  18. 4天快速落地团队内部的UI组件库
  19. 双目立体视觉 II:块匹配视差图计算
  20. 图片翻译成中文其实很简单,只需这几步

热门文章

  1. Quartus将sof文件生成.jic文件固化进flash
  2. 2022牛客多校9 BTwo Frogs(概率DP)
  3. 摩托梁念坚出任微软大中华区董事长兼CEO
  4. onedrive直链_OneDrive直链下载OneIndex
  5. Linux进程虚拟内存大 性能,Linux进程分析(一) 虚拟内存和物理内存
  6. org.springframework.core.styler包解读
  7. 人机交互期末复习笔记
  8. 在电脑上部署网站lls 浏览器上访问
  9. Pygame制作跳跃小球小游戏
  10. pandas入门与数据准备与简单筛选统计