Title

P3682 [CERC2016]自由的套娃 Free Figurines 题解

呜呜呜我是个只会模拟的蒟蒻~~~

题目传送门


大体思路:

模拟。

怎么模拟?

遍历每个套娃,如果该套娃不符合结局要求,那我们就移动他一次!

可以发现,直接正推条件很多,很难使用代码实现(其实是我不会),所以,我们换一种方法实现:倒推。

实现倒推:

倒推的思路与正推刚好相反,我们在输入的时候直接累计次数,最后遍历寻找不必要的操作,并将其减去。

为了使寻找无用操作变得简单,我们在累计次数时尽可能多的累计。如下:

for(int i=1;i<=n;i++){
        ans+=(a[i]!=0)+(b[i]!=0);
}

 

现在的重点便是如何找到不必要的操作并减去。

请看 1 号列表:

  1. 初始与结局相同即可不用操作。

  2. 查找时按照从小到大(从里到外)的顺序查找。

  3. 由于在累计时两者都是 0 的情况没有累计上,所以我们在减少时自然也不用减少 0 的情况了。

综上所述,给出寻找并减去这一片段的代码:

for(int i=1;i<=n;i++){
        if(f[i]==0) { //稍后解释
            int j;
            j=i;
            while(a[j]==b[j]&&a[j]!=0){
                ans=ans-2;
                j=a[j]; //继续寻找套在他外面的套娃,即他的父亲。
            }
        }
    }

 

其中,出现了 f 数组。

基于 1 号列表第 2 条,查找时的顺序要从内到外,所以我们用 fi 来存储第 i 个点肚子里是否有套娃,即他是不是父亲。如果他不是父亲,也就是说他是最里面的套娃,那我们就可以开始将不必要的操作减去了。

至于怎么存储嘛,有很多种方法,这里给出一种最通俗易懂的方法:

for(int i=1;i<=n;i++) f[i]=false; //初始化
    for(int i=1;i<=n;i++){
        cin>>a[i];
        f[a[i]]=true; //如果点 i 有父亲,那他的父亲肚子里就有套娃(那个套娃就是他),所以我们将他的父亲打标记。
    }
    for(int j=1;j<=n;j++){
        cin>>b[j]; //同上。
        f[b[j]]=true;
    }

 

至此,代码讲解完毕。

完整代码见下文。


代码:

#include<iostream>
using namespace std;
int n;
int a[100001]={0};
int b[100001]={0};
bool f[100001]={0};
int main(){
    cin>>n;
    long long ans=0;
    for(int i=1;i<=n;i++) f[i]=false; //f 数组初始化。
    for(int i=1;i<=n;i++){
        cin>>a[i];
        f[a[i]]=true; //使用 f 数组记录。
    }
    for(int j=1;j<=n;j++){
        cin>>b[j];
        f[b[j]]=true; //同上。
    }
    for(int i=1;i<=n;i++){
        ans+=(a[i]!=0)+(b[i]!=0); //累计答案。
    }
    for(int i=1;i<=n;i++){
        if(f[i]==0) { //寻找最里面的套娃。
            int j;
            j=i;
            while(a[j]==b[j]&&a[j]!=0){ //寻找不必要的操作。
                ans=ans-2;
                j=a[j];
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

 


总结:

只要你的思路清楚,跟题目走,就能做对!

posted @ 2022-07-20 09:24  Zilljy  阅读(25)  评论(1编辑  收藏  举报