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\)

posted @ 2024-09-08 20:00  Hanggoash  阅读(8)  评论(0编辑  收藏  举报
动态线条
动态线条end