ABC370 Review
ABC 370 Review
A
模拟题,过
B
模拟题,过
C
很明显的贪心思路是把需要更改的字母分为两类:改大和改小。
首先我们要明确的是要让输出的串尽量拥有小的字典序,且字典序比较的第一关键字是位置,第二是长度
所以对于改小的部分,改的位置越靠前我们就放在越前面操作;
对于改大的部分,改的位置越靠前我们反而放在越后面操作;
这样排序下来就会是最优的。
D
考试的时候打了一个链表发现假了,根本无法维护现有的。
想起之前做过一道 \(EC Final\) 的题也是通过 \(set\) 来维护同一行和同一列的关系,以后就要记性了。
简单来说就是通过二分查找\((lower\)_\(bound)\)来快速地确定应该修改的位置。
以前的知识
set<int> s;
s.size();
s.count(int val);
s.lower_bound(int val);
s.erase(auto it/int val);
新学到的知识
vector<set<int>> s(n);可以预先声明大小
auto erase = [&](int x, int y)
{
h[x].erase(y);
l[y].erase(x);
};
可以创建闭包在主函数使用临时函数 用 erase(x,y) 的格式调用
prev(it) 可以查找当前迭代器的上一个位置的迭代器(begin()小心越界)
其他都没有什么需要注意的地方了
更优秀的解法
运用 \(set\) 来维护的时间代价是 \(O(\log n)\) 的,实际上还有一种更加优秀的实现可以在接近 \(O(1)\) 的时间内完成。
想起来之前 2022绵阳CCPC 有一道水题也是给定 \(n\) 个人,然后会有干掉某个人的操作,需要你找到某个人右边最近的活人,当时用链表维护的,倒也没什么问题,因为只会查询活人。
但是这道题中被摧毁的点仍有可能在以后被查询到,所以链表就不适用了。
但有一种普适的算法就是 并查集 \(+\) 路径压缩,因为是变种并查集,所以维护的内容就不再是从属关系,所以查询函数要有小小的改动,注意要同时维护一个 \(vis\) 数组来记录有没有被摧毁,在并查集查询的时候照常路径压缩并且在 碰到 \(vis==1\) 的点的时候就返回。
注意这个题需要维护四个方向上的并查集,还要判断有没有出界,部分代码如下:
int fa_l[N],fa_r[N],fa_u[N],fa_d[N];
inline bool check(int x,int y)
{
return 1<=x&&x<=n&&1<=y&&y<=m;
}
inline int get_l(int x,int y)
{
if(!check(x,y))return -1;
if(!vis[x][y])return y;
return fa_l[y]=get_l(x,fa_l[y]);
}
E
连续区间划分问题
很明显的可以写出一个 \(O(n^2)\) 的暴力 \(dp\)
for(int i=1;i<=n;++i)
{
if(sum[i]!=k)dp[i]=1;
for(int j=1;j<i;++i)
{
if(sum[i]-sum[j]!=k)dp[i]+=dp[j];
}
}
这样做必定会超时,那么想办法把第二维度优化掉。
我们在对当前的 \(dp[i]\) 进行计算的时候,相当于会把之前所有的 \(dp\) 值都累加进去,然后减去满足 \(sum[i]-sum[j]==k\) 的 \(dp[j]\) 。
考虑到每个 \(dp[i]\) 值在被计算之后就不会被再次更改,其位置对应的 \(sum[i]\) 也是固定的。
可以将其试做一个二元组 \((sum[x],dp[x])\) ,我们统计所有 \(dp\) 的和,再减去对于当前位置 \(i\) 不符合情况的,也就是满足 \(sum[x]==sum[i]-k\) 的所有 \(dp[x]\) 。那么只需要开一个 \(map\) 来建立 $sum\rightarrow dp $ 的关系就行,代码如下。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
long long dp[N],sum[N];
int n,a,k;
template<typename T>inline void re(T &x)
{
x=0;
int f=1;char c=getchar();
while(!isdigit(c))
{
if(c=='-')f=-1;
c=getchar();
}
while(isdigit(c))
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
x*=f;
}
const int MOD=998244353;
inline void pre()
{
re(n),re(k);
for(int i=1;i<=n;++i)
{
re(a);
sum[i]=sum[i-1]+a;
if(sum[i]!=k)dp[i]=1;
}
}
long long dps;
map<int,int> rec;
//set<pair<int,int>> s;
//inline int find(int tar)
//{
// int ans=0;
// for(auto it=s.lower_bound(make_pair(tar,-1));it!=s.end()&&(*it).first==tar;it++)
// ans=(ans+(*it).second)%MOD;
// return ans;
//}
//inline void solve()
//{
// for(int i=1;i<=n;++i)
// {
// dp[i]=((dp[i]+dps)%MOD-find(sum[i]-k)+MOD)%MOD;
// dps=(dps+dp[i])%MOD;
// s.insert(make_pair(sum[i],dp[i]));
// }
// cout<<dp[n];
//}
inline void solve()
{
for(int i=1;i<=n;++i)
{
dp[i]=((dp[i]+dps)%MOD-rec[sum[i]-k]+MOD)%MOD;
dps=(dps+dp[i])%MOD;
rec[sum[i]]=(rec[sum[i]]+dp[i])%MOD;
}
cout<<dp[n];
}
signed main()
{
pre();
solve();
return 0;
}
中间那段注释掉的 \(set\) 我也不知道为什么会有一个点 \(wa\) 掉,虽然但是肯定能被卡 \(TLE\)
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18403332