题解:P10729 [NOISG 2023 Qualification] Dolls
XiaoZi_qwq · · 题解
P10729 [NOISG 2023 Qualification] Dolls 题解
前言
有点意思的思维题
题意分析
有若干个套娃,第
提两个小问题:
看到
遇到有点棘手的条件,第一步应该怎么做?
逐步分析
SubTask1:n\le200 :最初的思考
暴力解法:每次询问对
SubTask2:a_i 都是奇数:特殊情况
由于
但是这样是最优的吗?
显然对于每一个之前没出现过的
SubTask3:a_i 都不是 4 的倍数:提示
因为
相当明显,若干连续的
所以我们可以枚举这个组的长度(显然只有
思考一个小问题:如果有一个数出现在了两个组之间(如
正解:扩展
让我们沿着上面的思路继续思考,因为一个组的贡献只受其长度影响,那么我们只需要维护一个组的长度,而且由上面的枚举可以得知:一个长
那么现在我们回到了之前那个问题:当一个数出现在了两个组之间时,我们怎么计算贡献?
分析一下我们需要干什么:我们需要通过前面一个点和后面一个点分别找到它们所在组负责存储信息的点,还需要将它们合并。有查有并,这不就是并查集嘛!
通过并查集我们就可以快速进行组长的合并与维护。
到这里题目就分析完了,具体实现看代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
inline int read(){
register int x=0,f=1;
char c=getchar();
while(c<'0' || '9'<c) f=(c=='-')?-1:1,c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
const int N=500010;
int n,f[N],ans,sz[N];
bool vis[N];
inline int find(int x){ return f[x]=f[x]==x?x:find(f[x]);}
inline void un(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return;
f[fx]=fy;
sz[fy]+=sz[fx];
}
int main()
{
for(int i=1;i<=500000;i++) f[i]=i,sz[i]=1;
n=read();
for(int i=1,x;i<=n;i++){
x=read();
if(vis[x]) {printf("%d ",ans);continue;}
vis[x]=true;
//分类讨论 x 所处位置,不同情况有不同的合并方式
if(!vis[x+1] && !vis[x-1]) ans++;
else if(!vis[x+1]) un(x-1,x),ans+=sz[find(x)]%2;
else if(!vis[x-1]) un(x,x+1),ans+=sz[find(x)]%2;
else{
int f1=find(x-1),f2=find(x+1);
ans-=(sz[f1]+1)/2+(sz[f2]+1)/2;//消除影响
un(x-1,x),un(x,x+1);
ans+=(sz[find(x)]+1)/2;//再次计算贡献
}
printf("%d ",ans);
}
return 0;
}