首页 > 代码库 > C. Destroying Array 并查集,逆向思维

C. Destroying Array 并查集,逆向思维

用并查集维护线段,从后往前枚举没个删除的位置id[i]

那么,现在删除了这个,就是没有了的,但是上一个id[i + 1]就是还没删除的。

然后现在进行合并

int left = id[i + 1];(相当于每次都加入一个元素a[left])

它在这个位置,如果能和左右的合并,就是左右邻居都有元素,那么当然是合并最好,因为元素都是大于0的,越长越好。

合并的时候再记录一个数组sum[pos]表示以这个为根的总和是多少。
按并查集的思路更新整个并查集即可、

注意一点的就是,

要求ans[i]

那么ans[i + 1]是在没有id[i + 1]这个元素的前提下的最大值,现在有了这个元素,但是不见得会变大。

所以

ans[i] = max(ans[i + 1], sum[find(id[i + 1)]);

要和前一个比较一下

技术分享
#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm>using namespace std;#define inf (0x3f3f3f3f)typedef long long int LL;#include <iostream>#include <sstream>#include <vector>#include <set>#include <map>#include <queue>#include <string>const int maxn = 100000 + 20;int fa[maxn];LL sum[maxn];int find(int u) {    if (fa[u] == u) return fa[u];    else return fa[u] = find(fa[u]);}void merge(int x, int y) {    x = find(x);    y = find(y);    if (x != y) {        fa[y] = x;        sum[x] += sum[y];    }}int a[maxn];int id[maxn];bool has[maxn];LL ans[maxn];void work() {    int n;    scanf("%d", &n);    for (int i = 1; i <= n; ++i) fa[i] = i;    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);    for (int i = 1; i <= n; ++i) scanf("%d", &id[i]);    ans[n] = 0;    for (int i = n - 1; i >= 1; --i) {        int left = id[i + 1];        sum[left] = a[left];        if (has[left + 1]) {            merge(left, left + 1);        }        if (has[left - 1]) {            merge(left, left - 1);        }        ans[i] = max(ans[i + 1], sum[find(left)]);        has[left] = 1;    }    for (int i = 1; i <= n; ++i) {        printf("%I64d\n", ans[i]);    }}int main() {#ifdef local    freopen("data.txt","r",stdin);#endif    work();    return 0;}
View Code

 

C. Destroying Array 并查集,逆向思维