【带权并查集】How Many Answers Are Wrong HDU - 3038

How Many Answers Are Wrong HDU - 3038

题意:

有一个长度已知的整数串,给出一系列包含\(l,r\)的语句,表示\([l,r]\)这个区间的和。问有多少条语句是错误的。

思路:

多组输入!!!

数据只有\(2×10^5\)这么大,不用离散化了。

因为和这题差不多,所以就只简单地写一下关键点。

  • 区间处理

    为了能够连通,给出的闭区间\([u,v]\)要改成\((u-1,v]\)

  • 路径压缩的关系维护:

    假设已知03的和以及35的和,两个加起来就是0~5的和

\[S_{(u,root[u] ]}=S_{(u,fa[u]]}+S_{(fa[u],root[u]]}​ \]

  • 合并时的关系维护:

    假设已知03的和、47的和、04的和,求37的和。嗯,小学数学题。

    \[S_{(root(u),root(v)]} = S_{(u,v]} + S_{(v,root(v)]} - S_{(u,root(u)]} \]

  • 查询时的关系计算:

    做题的时候这里没想清楚就写上去了导致没有1A……唉

    计算的前提是两端点的根节点相同,也就是说,分别以这两个端点为左端点的两个区间有相同的右端点,所以:

    \[S_{(u,v]}=S_{(u,root]}-S_{(v,root]} \]

    然后将这个结果与给出的和比较是否相同就行了。

完整代码:

int n, m;
int rela[maxn], fa[maxn];

int find(int x) {
    if (fa[x] == -1) return x;
    int tmp = find(fa[x]);
    rela[x] = rela[x] + rela[fa[x]];
    return fa[x] = tmp;
}

bool merge(int u, int v, int s) {
    int r1 = find(u);
    int r2 = find(v);
    if (r1 == r2) {
        return s == rela[u] - rela[v];
    }
    else {
        fa[r1] = r2;
        rela[r1] = s + rela[v] - rela[u];
        //已知(0,3]为4,(5,7]为8,又(1,5]为7,则(3,7]为7+8-4=11
        return true;
    }
}

int main()
{
    //ios::sync_with_stdio(false);
    while (cin >> n >> m) {
        int ans = 0;
        memset(rela, 0, sizeof(rela));
        for (int i = 0; i <= n; i++) fa[i] = -1;
      //注意0也要初始化
        for (int i = 1; i <= m; i++) {
            int u, v, s;
            cin >> u >> v >> s;
            if (u > v) swap(u, v);
            if (!merge(u - 1, v, s)) ans++;
        }
        cout << ans << endl;
    }
    return 0;
}
posted @ 2020-08-09 23:56  StreamAzure  阅读(97)  评论(0编辑  收藏  举报