2024.11.23模拟赛(*^▽^*)
加密版:困困,困困困困困。困困困困困困困困困困困困困困困困困,困困困困困困困困困困困。困困困,困困困困困困困困困困,困困困困困困困困困困困困困困困困困困困困困,困困。困困困困,困困困困!
今天,模拟赛还没开始多久,就闻到了弥漫在空气中的糊味。于是,整个机房一起(?)冲到操场 看热闹 观察情况,不过十分钟后也差不多回去了。然后就开始打。T1题意叙述不清+简单,我都怀疑我是不是想假了;T2刚开始想成贪心了,但总感觉不对劲,及时换成了\(dp\);T3?我不道啊,推了下样例一不小心性质就出来了,打了特殊样例,但是我为什么不打暴力!!!为什么啊wwwwww;真是\(dij\)打多了,看到T4就想到\(dij\)的暴力写法了,然后又手推了下特殊样例,虽然时间耗得多了点,但分到手了。最后T1 100分,T2 70分,T3 12分,T4 30分,总分212,未挂分。
T1【公交车】
题目大意:
实在是不会翻译了……直接出吧
给出\(n\)个元素之间的\(m\)个关系,求节点数减去连通块数。\((1\leqslant n\leqslant 10^{5},1\leqslant m\leqslant 2\times 10^{5})\)
解题思路:
并查集直接做完了。我还搞什么带权并查集啊喂
代码太史就不放了
T2【自行车】
题目大意:
给出两个\(1\sim n\)的排列\(a,b\)。进行若干次操作,要求:若目前两个排列的第一个数不同,那么可以一次性删除掉两个排列的第一个数;若两个排列的第一个数相同,那么只能选择一个排列的第一个数删除。求清空两个排列的最小操作次数。\((1\leqslant n\leqslant 10^{6})\)
解题思路:
这道题我一定要多看多看再多看……
容易想到\(dp\)转移方程:
但这样的转移是\(O(n^{2})\),考虑优化。
显然,我并没有想出怎样优化
我们可以注意力惊人地注意到,满足\(a_{i}=b_{j}\)状态的只有\(n\)个,我们称它是特殊状态,称\(a_{i}\neq b_{j}\)是普通状态。
经过思考,我们会发现:对于普通状态\(f_{i,j}\),它一定是从\(f_{i-1,j-1}\),\(f_{i-2,j-2}\)…一直到特殊状态\(f_{i-t,j-t}\)转移而来的。也就是说,每个普通状态一定是通过某个特殊状态+某个固定值转移而来的。所以,只要处理出所有特殊状态的值,就可以得到答案了。
考虑设状态\(f_{k}\) 表示对于\(k=a_{i}=b_{j}\) 的特殊状态的转移答案,转移方程即$$f_{i}=min(getF_{i-1 , vis[a_{ i } ] } ,getF_{i,vis_[a_{i}]-1})+1$$这与最开始的状态转移方程一致,其中的\(getF\)转移时计算即可。
注意到在从特殊状态转移到普通状态时,\(i,j\)两维的差值始终不变,所以我们可以记\(g_{j-i}=\{i,f_{i}\}\)以记录\(i,j\)的差值。\(getF_{i,j}\)的转移方程即
神奇的代码
#incIude <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int N=1e6+5;
int n;
int a[N],b[N];
int vis[N];
int f[N];
pii g[N<<1];//记录相应差值的特殊状态
int get(int x,int y)
{
if (g[y-x+n].first==0) return max(x,y);//以防万一,搞个边界
return g[y-x+n].second+(x-g[y-x+n].first);
}
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++)
{
scanf("%lld",&b[i]);
vis[b[i]]=i;
}
for (int i=1;i<=n;i++)
{
f[i]=min(get(i-1,vis[a[i]]),get(i,vis[a[i]]-1))+1;//+n防溢出
g[vis[a[i]]-i+n]=mp(i,f[i]);
}
printf("%lld",get(n,n));
return 0;
}