0%

思路讲解

给一个数列,每次询问一个区间内有没有一个数出现次数超过一半。

AC代码

https://www.luogu.com.cn/record/218724261

心路历程(WA,TLE,MLE……)

我超,这个POI的数据还是挺严格的,我的这个主席树codeforces和洛谷模版都过了,就这道题目没有过。

1
2
3
1 1
1
1 1

黑体部分,要放在比较下面,不要放在版本更新的上面,否则可能未版本更新就出去了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 处理静态区间第 k 小问题中,因为权值线段树,x=1
void add(int node,int ver,int p,ll x){
clone(node);
sum[idx]+=x;
// 如果当前遍历到的点是一个版本的根,那么现在的点idx就是目前这个版本的根
if(root[ver]==node){
++Cver;
root[Cver]=idx;
}
if(p==L[node] && p==R[node]){
return;
}
if(p>=L[ls[node]] && p<=R[ls[node]]){
int nals=ls[idx];
ls[idx]=idx+1;
add(nals,ver,p,x);
}else{
int nars=rs[idx];
rs[idx]=idx+1;
add(nars,ver,p,x);
}
}

思路讲解

主要参考这段代码

https://github.com/algorithmzuo/algorithm-journey/blob/main/src/class157/Code02_PointPersistent2.java

参考题解

【算法讲解157【挺难】可持久化线段树和标记永久化】 【精准空降到 45:11】 https://www.bilibili.com/video/BV1bSc6eREi4/?share_source=copy_web&vd_source=6ca0bc05e7d6f39b07c1afd464edae37&t=2711

AC代码

https://www.luogu.com.cn/record/218615977

心路历程(WA,TLE,MLE……)

https://www.luogu.com.cn/record/218612872

这个 k-lsum 要小心,记得要加,因为往r走相当于抛掉了一些东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// u v 为node,l,r为区间,k为区间第k小
int query(int u,int v,int l,int r,int k){
if(l==r){
return l;
}
// 我们这样就知道了
int lsum=sum[ls[v]]-sum[ls[u]];
int mid=l+r>>1;
if(lsum>=k){
return query(ls[u],ls[v],l,mid,k);
}else{ // 这个 k-lsum 要小心,记得要加,因为往r走相当于抛掉了一些东西
return query(rs[u],rs[v],mid+1,r,k-lsum);
}
}

思路讲解

子序列自动机,最重要的是next数组,可以找到从i位置开始,字符下一次出现在哪个位置(下标)(包括i位置)

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
struct seqAuto{	// 子序列自动机 subsequence automaton 
// 可以处理大小写字母,数字,但是可能要改一下base以及next
vector<vector<int>> next;
// 从i位置开始,字符下一次出现在哪个位置(下标)(包括i位置)
char base='a';
seqAuto(const string &s){
next.assign(SZ(s)+5,vector<int>(28,-1));
ROF(i,SZ(s)-1,0){
FOR(j,0,26){
next[i][j]=next[i+1][j];
}
next[i][s[i]-base]=i;
}
}
inline bool isFind(const string &s){ // 传入substr时,必须为const属性
ll idx=0;
FOR(i,0,SZ(s)-1){
if(next[idx][s[i]-base]!=-1){
idx=next[idx][s[i]-base]+1;
}else{
return false;
}
}
return true;
}
};

然后用双指针方式枚举子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FOR(i,0,SZ(S)-1){	// 双指针
FOR(j,bp,SZ(S)){
assert(j>=i);
ll len=j-i;
if(!sat.isFind(S.substr(i,len))){ // pos,len
bp=j;
if(ans.empty() || len-1>SZ(ans.back())){
ans.clear();
ans.pb(S.substr(i,len-1));
}else if(len-1==SZ(ans.back()) ){
ans.pb(S.substr(i,len-1));
}
break;
}
}
}

AC代码

https://www.luogu.com.cn/record/218545124

心路历程(WA,TLE,MLE……)

思路讲解

使用递推方法加快Sum预处理速度。

image

递推代码。

1
2
3
4
5
6
7
8
ROF(k,log2(N)-1,0){  // 
FOR(i,0,pow2[k]-1){
// assert(pow2[k]+i<SZ(Sum[k+1]));
Sum[k][i]=Sum[k+1][i]+Sum[k+1][pow2[k]+i];
Sum[k][i]%=mod;
Cnt[k][i]=Cnt[k+1][i]+Cnt[k+1][pow2[k]+i];
}
}

AC代码

https://www.luogu.com.cn/record/218505364

心路历程(WA,TLE,MLE……)

赛时因为没有采用递推所以代码比较慢。