[CSP-S模拟测试]:platform(后缀数组+二分+线段树)
题目传送门
题目描述
走过奈何桥有一个名叫望乡台的土台,望乡台有个名曰孟婆的老妇人在卖孟婆汤。一生爱恨情仇,一世浮沉得失,都可以随这碗孟婆汤遗忘得干干净净。
现在有$n$碗孟婆汤摆成一排,汤的品种不超过$26$种,因此我们用小写字母$a\sim z$来表示一种汤,每碗汤还有一个权值${val}_i$。
你需要选出若干碗连续摆放的汤喝下去,这些汤必须满足下列条件:
$\alpha.$至少有一碗汤。
$\beta.$这个子串(也就是那些汤)在原串中的所有子串中的字典序降序排名等于这一段汤的权值之和。
现在你需要知道有多少种选汤的方案。
注意出现在不同位置、本质相同的子串的排名是相同的且要重复计算方案数,如$"aaa"$这个串,排名为$1$的子串为$"aaa"$,出现了一次;排名为$2$的子串为$"aa"$,出现了两次;排名为$3$的子串为$"a"$,出现了三次。(若还没明白题意的可以看样例$1,3$的解释)。
输入格式
第一行一个长度为$n$的由小写字母组成的字符串,每个字符代表一碗汤。
第二行$n$个非负整数,表示${val}_i$。
输出格式
一行一个整数,表示能被选的子串个数$S$。
接下来$S$行每行两个整数$L,R$,分别表示每个可选子串的左端点与右端点,按照左端点升序为第一关键字,右端点升序为第二关键字排序。
样例
样例输入1:
abcd
10 0 1 1
样例输出1:
3
1 1
3 4
4 4
样例输入2:
aaaa
1 1 1 1
样例输出2:
0
样例输入3:
aaa
1 1 1
样例输出3:
2
1 2
2 3
样例输入4:
abdacdbcecbd
1 3 1 3 3 4 2 2 4 2 1 1
样例输出4:
2
3 8
9 9
数据范围与提示
样例$1$解释:
我们把所有的子串按字典序从大到小排名:$d,cd,c,bcd,bc,b,abcd,abc,ab,a$。
那么串$d$的排名为$1$(第一大),权值和为$1$,可以被选。
串$cd$的排名为$2$,权值和为$2$,可以被选。
串$a$的排名为$10$,权值和为$10$可以被选。
其他串则不满足这个条件,故有三个串可以被选。
样例$3$解释:
串$a$的排名是$3$,权值和都是$1$。
串$aa$的排名是$2$,权值和都是$2$,共有两个串$aa$,位置分别为$1\ 2$和$2\ 3$。
串$aaa$的排名是$1$,权值和都是$3$。
数据范围:
本题共有$10$测试点:
对于第$1$个测试点,满足$n\leqslant 50$。
对于第$2,3$个测试点,满足$n\leqslant 1,000$。
对于第$4$个测试点,满足字符串只由一种字符组成,$n\leqslant 50,000$。
对于第$5$个测试点,满足所有汤的权值相同,$n\leqslant 50,000$。
对于第$6,7$个测试点,满足$n\leqslant 50,000$。
对于第$8,9,10$个测试点,满足$n\leqslant 200,000$。
保证$0\leqslant {val}_i\leqslant 1,000$,且每个测试点满足要求的子串个数不超过$200,000$个。
题解
$30\%$算法:
我们可以暴力统计所有的区间,然后暴力排序,利用前缀和优化$val$的统计,总之就是很暴力。
时间复杂度:$\Theta(n^2)$。
期望得分:$30$分。
实际得分:$10\sim 30$分。
$100\%$算法:
一般这种字符串又没有匹配的问题我们就来考虑后缀数组好啦。
还是先来偷偷的观察一下数据范围,发现${val}_i>0$,那么对于每一个左端点,如果把它的子串按排名单调下降排,那么权值和将是一个单调不下降的,也就是说,我们可以通过二分找到是否有交点,那么问题就转化问如何快速查找这个排名了。
在后缀数组中,一个串的本质不同的子串有$\sum \limits_{i=1}^n n-sa[i]-height[i]+1$个,为方便,设这个数组为$c$,对于每个左端点$l$,设它的一个子串的右端点为$r$,那么这个子串的排名即为$c[n]-c[x[sa[l]-1]-r+l+height[x[sa[l]]]$,然而这个式子仅适用于该子串不是与当前串的前一个排名的串的LCP的子串时成立。可能会有两个串相同怎么办?
利用线段树为何这个位置所在增加的$LCP$的第一个位置,然后通过这个位置计算出当前位置的排名即可。
然后我们还能发现,其实$LCP$部分的排名是单调下降的,这样我们可以在处理非$LCP$部分的时候一起二分。
还有一种做法,是在后缀数组上$RMQ+$二分,但是我不会。
时间复杂度:$\Theta(n\log^2n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
$30\%$算法:
- #include<bits/stdc++.h>
- using namespace std;
- struct rec
- {
- int l,r,w;
- }e[1000001];
- int n;
- int a[200001];
- int val[200001];
- char ch[200001];
- int sum,cnt;
- pair<int,int> que[200001];
- void pre_work()
- {
- for(int i=1;i<=n;i++)
- for(int j=i;j<=n;j++)
- {
- e[++cnt].l=i;
- e[cnt].r=j;
- e[cnt].w=val[j]-val[i-1];
- }
- }
- bool cmp(rec x,rec y)
- {
- for(int i=0;i<min(x.r-x.l+1,y.r-y.l+1);i++)
- if(a[x.l+i]!=a[y.l+i])return a[x.l+i]>a[y.l+i];
- return x.r-x.l>y.r-y.l;
- }
- bool judge(int x)
- {
- unsigned long long hash1=0,hash2=0;
- for(int i=e[x].l;i<=e[x].r;i++)
- hash1=hash1*13131+a[i];
- for(int i=e[x-1].l;i<=e[x-1].r;i++)
- hash2=hash2*13131+a[i];
- if(hash1==hash2)return 0;
- return 1;
- }
- int main()
- {
- scanf("%s",ch+1);
- n=strlen(ch+1);
- for(int i=1;i<=n;i++)
- {
- scanf("%d",&val[i]);
- a[i]=ch[i]-'a'+1;
- val[i]+=val[i-1];
- }
- pre_work();
- sort(e+1,e+cnt+1,cmp);
- int hsw=0;
- for(int i=1;i<=cnt;i++)
- {
- if(judge(i))hsw++;
- if(hsw==e[i].w)
- que[++sum]=make_pair(e[i].l,e[i].r);
- }
- sort(que+1,que+sum+1);
- cout<<sum<<endl;
- for(int i=1;i<=sum;i++)
- printf("%d %d\n",que[i].first,que[i].second);
- return 0;
- }
$100\%$算法:
- #include<bits/stdc++.h>
- #define L(x) x<<1
- #define R(x) x<<1|1
- using namespace std;
- int n;
- int a[200001];
- int val[200001];
- char ch[200001];
- int x[200001],y[200001],sa[200001];
- long long c[200001],h[200001];
- int tr[800001];
- int hsw[200001];
- pair<int,int> ans[200001];
- int wzc;
- void change(int x,int l,int r,int L,int R,int w)
- {
- if(L>r||R<l)return;
- if(L<=l&&r<=R){tr[x]=w+1;return;};
- int mid=(l+r)>>1;
- if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];
- change(L(x),l,mid,L,R,w);
- change(R(x),mid+1,r,L,R,w);
- tr[x]=tr[L(x)]==tr[R(x)]?tr[L(x)]:0;
- }
- int ask(int x,int l,int r,int w)
- {
- if(tr[x])return tr[x]-1;
- int mid=(l+r)>>1;
- if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];(x,l,r);
- if(w<=mid)return ask(L(x),l,mid,w);
- else return ask(R(x),mid+1,r,w);
- }
- void get_sa()
- {
- int m=30;
- memset(c,0,sizeof(c));
- for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
- for(int i=1;i<=m;i++)c[i]+=c[i-1];
- for(int i=n;i;i--)sa[c[x[i]]--]=i;
- for(int len=1;len<=n;len<<=1)
- {
- int p=0;
- for(int i=n-len+1;i<=n;i++)y[++p]=i;
- for(int i=1;i<=n;i++)if(sa[i]>len)y[++p]=sa[i]-len;
- memset(c,0,sizeof(c));
- for(int i=1;i<=n;i++)c[x[y[i]]]++;
- for(int i=1;i<=m;i++)c[i]+=c[i-1];
- for(int i=n;i;i--)sa[c[x[y[i]]]--]=y[i];
- for(int i=1;i<=n;i++)x[i]^=y[i]^=x[i]^=y[i];
- p=1;x[sa[1]]=1;
- for(int i=2;i<=n;i++)
- x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len]?p:++p;
- if(p>=n)break;
- m=p;
- }
- }
- void get_height()
- {
- int len=0;
- for(int i=1;i<=n;h[x[i++]]=len,len=len?len-1:0)
- if(x[i])while(a[i+len]==a[sa[x[i]-1]+len])len++;
- }
- long long get_ra(int l,int r)
- {
- if(r-l+1>h[x[l]])return c[n]-c[x[l]-1]-r+l+h[x[l]];
- int flag=ask(1,1,n,r-l+1);
- return hsw[flag]+flag-r+l-1;
- }
- int main()
- {
- scanf("%s",ch+1);
- n=strlen(ch+1);
- for(int i=1;i<=n;i++)
- {
- scanf("%d",&val[i]);
- a[i]=ch[i]-'a'+1;
- val[i]+=val[i-1];
- }
- get_sa();
- get_height();
- for(int i=1;i<=n;i++)
- c[i]=c[i-1]+n-sa[i]-h[i]+1;
- for(int i=1;i<=n;i++)
- {
- int lft=sa[i],rht=n;
- while(lft<=rht)
- {
- int mid=(lft+rht)>>1;
- if(val[mid]-val[sa[i]-1]<get_ra(sa[i],mid))lft=mid+1;
- else rht=mid-1;
- }
- if(val[lft]-val[sa[i]-1]==get_ra(sa[i],lft))ans[++wzc]=make_pair(sa[i],lft);
- if(h[i]<h[i+1])
- {
- hsw[h[i]+1]=get_ra(sa[i],sa[i]+h[i]);
- change(1,1,n,h[i]+1,h[i+1],h[i]+1);
- }
- }
- sort(ans+1,ans+wzc+1);
- printf("%d\n",wzc);
- for(int i=1;i<=wzc;i++)
- printf("%d %d\n",ans[i].first,ans[i].second);
- return 0;
- }
rp++
[CSP-S模拟测试]:platform(后缀数组+二分+线段树)的更多相关文章
- K-th occurrence HDU - 6704 (后缀数组+二分线段树+主席树)
大意: 给定串s, q个询问(l,r,k), 求子串s[l,r]的第kk次出现位置. 这是一篇很好的题解: https://blog.csdn.net/sdauguanweihong/article/ ...
- HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5008 Description In this problem, you are given ...
- 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 657 Solved: 274[Su ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
- 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ
[BZOJ4556][Tjoi2016&Heoi2016]字符串 Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一 ...
- CF1063F. String Journey(后缀数组+线段树)
题目链接 https://codeforces.com/contest/1063/problem/F 题解 虽然本题有时间复杂度较高但非常好写的做法...... 首先,若答案为 \(k\),则一定存在 ...
- 树状数组+二分||线段树 HDOJ 5493 Queue
题目传送门 题意:已知每个人的独一无二的身高以及排在他前面或者后面比他高的人数,问身高字典序最小的排法 分析:首先对身高从矮到高排序,那么可以知道每个人有多少人的身高比他高,那么取较小值(k[i], ...
- POJ 2892 Tunnel Warfare || HDU 1540(树状数组+二分 || 线段树的单点更新+区间查询)
点我看题目 题意 :N个村子连成一条线,相邻的村子都有直接的地道进行相连,不相连的都由地道间接相连,三个命令,D x,表示x村庄被摧毁,R ,表示最后被摧毁的村庄已经重建了,Q x表示,与x直接或间 ...
- BZOJ 3173 最长上升子序列(树状数组+二分+线段树)
给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? 由于序列是顺序插入的,所以当前插入的数字对之 ...
随机推荐
- 快速测试端口的连通性(HTTP/HTTPS)
ping 仅限 80 端口,命令中无法指定端口: C:\Users\Administrator>ping kikakika.com 遗失对主机的连接. 正在 Ping kikakika.com ...
- 06:(h5*)Vue第六天
目录 1:iView 2: element 3: vuex 正文 1:i-view 1:装包 npm install view-design --save 2:导包 import ViewUI f ...
- hdu4352 XHXJ's LIS(数位dp)
题目传送门 XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- DMA的认识
DMA的简单了解与认识 DMA就是为了减轻CPU的负担来设置的存储方式.当从外设取到的数据就不需要经过内核操作,而是通过DMA直接把外设的数据放到内存SRAM中,这样就会减少CPU的负担,让CPU在此 ...
- jq中的ajax传参
一. jq中的Ajax传参有两种 1.通过url地址来传参 2.通过data来传递参数 1. url来传递参数 function GetQuery(id) { | ...
- H5中图片按照比例收缩,放大
需求:后台传过来的图片不能够压缩,即使部分被截取也可以 传统方案:设置img元素如下: width: auto; height: auto; max-width: 100%; max-height: ...
- Oracle 存储过程--01
1.简单的例子 /* 1.求圆的面积 */ create or replace procedure proc_1 is pi constant number(9,7):=3.1415927; radi ...
- mysql授予权限
grant show databases on *.* to 'asg'@'%'; grant select on *.* to 'asg'@'%'; grant show view on *.* t ...
- 洛谷3321 SDOI2015 序列统计
懒得放传送[大雾 有趣的一道题 前几天刚好听到Creed_神犇讲到相乘转原根变成卷积的形式 看到这道题当然就会做了啊w 对于m很小 我们暴力找原根 如果你不会找原根的话 出门左转百度qwq 找到原根以 ...
- SPOJ287 NETADMIN - Smart Network Administrator
传送门[洛谷] 常见套路? 关键点连新建汇点 流量1 源点1 原图中的边 二分流量. 二分+判满流 做完了. 附代码. #include<cstdio> #include<cstri ...