2019南昌网络赛Hello 2019(cf 750E)线段树_算法日常[24/100]

题目小趣事

我在比赛一开场就开始看这题,然后没想出怎么写,12:07我校一个大佬lxc说他拿下了这题的一血,这是一个原题,cf 750E(我只能说:大佬都是移动题库!)然后做完另一个签到题之后,成为第一名,带榜了

赛后发现1

Hello 2019 的提交情况如下:
通过率:5.66% 正确提交 / 总提交:217 / 3837

达成学校带崩他校节奏的成就

赛后发现2

这题**tourist(codeforces霸榜第一,ACM world final 4小时ak第一人,被评为全球最厉害的十个程序员之一(和c语言之父这些人在一个榜单!))**当年比赛的时候也没有做出来!然后大家说自己竟然尝试了一下当年T神都没有做出来的题目

Tshen

题目链接

计蒜客传送门
codeforces 750E

题意

给你一个串,然后多次询问左右区间,看删除多少个字符能使得这个区间内有9102,而没有8102的subsequence(codeforces中是有2017而没有2016)

题解

  • 肉眼做法,表层理解,很容易看出只要删除8
  • 因为8放在第一个不好处理,所以改成翻转string,并且翻转l,r,从而变成判断有2019没有2018,
  • 因为每次询问一个区间,所以需要把dp状态扔到某个数据结构上,所以我们考虑线段树
  • 线段树更新的时候是拿两段的信息合并,所以不能像做1~n的dp那样记录状态

考虑2017之间的间隔:

| 2 | 0 | 1 | 9 |

0   1   2   3   4

  • 线段树的每个节点存一个矩阵A.$mat_{ij}$表示使原串的子序列包含2019中第i个间隔到第j个间隔组成的子串,但不严格包含它的子序列最少需要删除的数字、

  • 转移是显然的,和区间dp一样。枚举区间,枚举中间点,然后转移就好了。

  • 考虑初值问题,显然的是非2、0、1、9、8的数字对答案不影响,所以令$a_{ii}$=0,$a_{ij}$=N(取不到就行) (i!=j)

  • 考虑当前数字是2的时候,如果我希望只包含子串[0,0](这里表示两个间隔间的子串),那么就必须删掉这个2,故$a_{00}$=1(可以理解为不想要成为2019是不思进取的行为,所以付出代价–>这样可以在后面处理的时候淘汰掉这些大的状态值),如果希望包含子串[0,1],那么什么都不用做,所以$a_{01}$=0。对于0、1、7同理。

  • 考虑当前数字是8的时候,那么遇到子串[i,3]希望转移回自己(不想接受8来到达4状态),那么需要付出1的代价,因为否则会包含子序列”2018”,同样如果遇到子串[i,4]希望转移回自己(不想接受8来到达4状态),那么也需要付出1的代价(区间如果询问到这里就是真正要删除的数字了,因为4达到状态终点了)。

AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
char s[N];
struct Node {
int mat[5][5];
Node operator + (Node x) {
Node ans;
for(int i=0;i<5;++i) {
for(int j=0;j<5;++j) {
ans.mat[i][j]=N;
for(int k=0;k<5;++k) {
ans.mat[i][j]=min(ans.mat[i][j],mat[i][k]+x.mat[k][j]);
}
}
}
return ans;
}
}a[N<<2];
void build(int l,int r,int x) {
if(l==r) {
for(int i=0;i<5;++i) {
for(int j=0;j<5;++j) {
a[x].mat[i][j]=(i==j)?0:N;
}
}
if(s[l]=='2') a[x].mat[0][1]=0,a[x].mat[0][0]=1;
if(s[l]=='0') a[x].mat[1][2]=0,a[x].mat[1][1]=1;
if(s[l]=='1') a[x].mat[2][3]=0,a[x].mat[2][2]=1;
if(s[l]=='9') a[x].mat[3][4]=0,a[x].mat[3][3]=1;
if(s[l]=='8') a[x].mat[3][3]=1,a[x].mat[4][4]=1;
return ;
}
int m=l+r>>1;
build(l,m,x<<1);build(m+1,r,x<<1|1);
a[x]=a[x<<1]+a[x<<1|1];
}
Node query(int l,int r,int L,int R,int x) {
if(L<=l&&r<=R) return a[x];
int m=l+r>>1;
if(m<L) return query(m+1,r,L,R,x<<1|1);
if(m>=R) return query(l,m,L,R,x<<1);
return query(l,m,L,R,x<<1)+query(m+1,r,L,R,x<<1|1);
}
int main() {
int n,q;
while(~scanf("%d%d%s",&n,&q,s+1)) {
for(int i=1;i<=n/2;i++){
swap(s[i],s[n-i+1]);
}
build(1,n,1);
while(q--) {
int l,r;int tl,tr;
scanf("%d%d",&tl,&tr);
r = n-tl+1,l = n - tr + 1;

int ans=query(1,n,l,r,1).mat[0][4];
if(ans==N) ans=-1;
printf("%d\n",ans);
}
}
return 0;
}

参考链接

阿波罗2003

每天一句叨叨

这个世界,想不想要永远不是最重要的,重要的,是要不要得起。

就像每个人都想成功,但是不是每个人都承担得起成功的代价