洛谷P2342叠积木解题报告

叠积木

洛谷P2342


技术统计

难度 提高+/省选-

用时 30min

提交次数 3

unaccept 次数 1

ac次数 2


题意概括

我们首先将所有的积木想象为一个一个的栈,要求我们维护一下个操作

  1. 将两个栈合并
  2. 查询i号点在其所在的栈的高度( 到栈底有多少个元素 )

数据范围

1 < = p < = 1 0 5 1<=p<=10^5 1<=p<=105



解法、

知识点

  1. 并查集
  2. 加权并查集(嘤)

解法概括

用三个数组:head[ i ],cnt[ i ],fa[ i ],分别维护i号点所在的栈的栈顶元素、i号点到栈底的高度、i号点所在的栈的栈底元素。我们设i号点所在的栈为栈A,j号点(请先不要疑惑j号点是哪里来的,这其实就是设了一个未知数)所在的栈的为栈B。

head[i]的更新

当我们将栈A放在栈B上面的时候,显然栈A的栈顶变为栈B的栈顶,所以head[j]=head[i]。

cnt[i]的更新

我们使用一种类似于线段树中懒标记的思想:用的时候再更新。
但是我们不可能重新开其他数组记录所有的合并信息(好像可以,但会很浪费空间),所以我们就先只更新原来栈A的栈底k,让cnt[k]=cnt[head[j]]+1,等询问到的时候,我们用find函数找爸爸的特性来更新。

fa[i]的更新… …

就是普通并查集的更新了啊

坑点

  1. 在维护点x到栈底的距离的时候,可以在find函数中修改,但一定要加cnt[head[y]]
  2. 其实没必要维护一个sum[i]来记录以i为栈底的栈的大小,显然cnt[head[i]]==sum[i]
  3. 这里提一下用递归找父亲和while找父亲的区别:
    一般while会跑得更快一些(实测,也可能是我的递归版本太渣),但是相对于递归版本,个人认为while不如递归版本灵活,我在打加权并查集的时候一般都用递归。

代码实现

#include
#define maxn 30010
using namespace std;
int n;
int head[maxn],cnt[maxn],fa[maxn];
int find(int a)
{
    if(a==fa[a])return a;
    else 
    {
        int fat=fa[a];
        fa[a]=find(fat);
        cnt[a]+=cnt[fat];
        head[a]=head[fat];
        return fa[a];
    }
}
void merge(int x,int y)
{
    fa[x]=y;
    find(head[y]);
    cnt[x]=cnt[head[y]]+1;
    head[y]=head[x];
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>n;
    for(int i=1;i<=maxn;i++)fa[i]=head[i]=i;
    for(int i=1;i<=n;i++)
    {
        char a;
        int x,y;
        cin>>a;
        if(a=='M')
        {
            cin>>x>>y;
            int fx=find(x),fy=find(y);
            if(fx!=fy) merge(fx,fy);
        }
        else 
        {
            cin>>x;
            find(x);
            cout<<cnt[x]<<endl;
        }
    }
    return 0;
}


类似题目

  1. 银河英雄传说(就是一个题)
  2. How Many Answers Are Wrong

你可能感兴趣的:(刷题)