P9753 [CSP-S 2023] 消消乐 题解(不用栈也不用map)
不用栈也不用map。
考虑预处理。 处理 $a$ 数组,每次走到一个位置 $i$,往前搜索。 当前位置不等于 $i$ 则通过这个位置继续往前查找。一直到当前位置等于 $i$,或者到达最前端则停止。 接下来进行第二次处理。 由于已经对 $a$ 进行过预处理,在计算时只需要从有值的点分别往前统计即可。 最后求一遍和。
感谢学弟 @kibi 的优化!
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2000010;
int n, a[N], f[N],tot;
char s[N];
signed main() {
memset(a, -1, sizeof a);
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i];
for(int i=1;i<=n;i++) a[i]=i-1;
for(int i=1;i<=n;i++){
while(s[a[i]]!=s[i])
{
a[i]=a[a[i]]-1;
if(a[i]<=-1) break;
}
}
// for(int i=1;i<=n;i++) cout<<a[i]<<' ';
// cout<<endl;
for(int i=1;i<=n;i++) {
if(a[i]>0) f[i]=f[a[i]-1]+1;
}
for(int i=1;i<=n;i++) {
tot+=f[i];
}
cout<<tot<<endl;
}
加强版:CF1223F Stack Exterminable Arrays - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
直接使用上面的思路,发现前四个点跑得飞快,但tle on #5。
考虑在上面的思路的基础上进行剪枝。
观察每一次往回预处理上一个出现位置的情况。
若查找位置已经小于当前这个数字最早出现的坐标,此时可以将这个值改为-1然后break。
这样就少了很多重复查询的过程,不容易被卡到 $O(n^2)$。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 300010;
int n, a[N], f[N],tot;
int s[N];
int t;
int vis[N];
signed main() {
memset(vis, 0x3f3f3f3f, sizeof vis);
memset(a,-1,sizeof a);
cin>>t;
while(t--)
{
tot=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i],f[i]=0;
vis[s[i]]=min(vis[s[i]],i);
}
f[0]=0;
for(int i=1;i<=n;i++) a[i]=i-1;
for(int i=1;i<=n;i++){
while(s[a[i]]!=s[i])
{
a[i]=a[a[i]]-1;
if(a[i]<=-1) break;
if(vis[s[i]]>a[i]){
a[i]=-1;
break;
}
}
}
for(int i=1;i<=n;i++) {
if(a[i]>0) f[i]=f[a[i]-1]+1;
}
for(int i=1;i<=n;i++) {
tot+=f[i];
}
cout<<tot<<endl;
}
}

浙公网安备 33010602011771号