分组 (并查集)

来源:互联网 发布:浙江网络作家协会主席 编辑:程序博客网 时间:2024/06/03 20:38

分组

10.23

问题可以转化为,从后往前,选择一段最长的合法区间并分割,重复进行直到完成为止。
这里写图片描述这里写图片描述这里写图片描述

从std中收获一种神奇的并查集写法(下面隐藏处),准备研究研究。(按秩合并优化路径压缩??还只有一个数组??看起来就很优)

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#define N 150000#define LL long longusing namespace std;inline int read(){    int x = 0, f = 1; char ch = getchar();    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); }    while(ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); }    return x * f;}int n, idc = 0, K;int a[N], ans[N];bool vis[N], dvis[N], issqr[N * 2];int f[N * 2];int getfa(int x) {    //return f[x] > 0 ? (f[x] = getfa(f[x])) : x;    return f[x] == x ? x : (f[x] = getfa(f[x]));}void unionn(int u, int v) {    u = getfa(u), v = getfa(v);    if (u != v) {        //if (f[u] > f[v]) swap(u, v);        //f[u] += f[v];        f[v] = u;    }}bool check(int u, int v) {    int s1 = getfa(u), s2 = getfa(u + N);//反点     int t1 = getfa(v), t2 = getfa(v + N);    if (s1 == t1) return 1;    if (s2 == t2) return 1;    unionn(s1, t2); unionn(s2, t1);    return 0;}void solve1() {    for (int i=n, j=n; i;) {        for (bool flag=1; j; j--) {            for (int k=1; k*k-a[j] < N; k++) {                if (k*k-a[j] <= 0) continue;                if ( vis[k*k-a[j]] ) { flag = 0; break;}             }            if ( !flag ) break;            vis[a[j]] = 1;        }        if ( !j ) break;        ans[++idc] = j;        for ( ; i>j; i--) vis[a[i]] = 0;    }}void solve2() {    //memset(f, -1, sizeof f);    for(int i=1; i<2*N; i++) f[i] = i;    for(int i=1; i*i<2*N; i++) issqr[i*i] = 1;    for(int i=n, j=n; i; ) {        for( ; j; j--) {            bool flag = 1;            if ( vis[a[j]] ) {                if ( issqr[a[j] + a[j]] ) {//当前堆内冲突                     if ( dvis[a[j]] ) break;//区间内已有两个相同数字                     for (int k=1; k*k-a[j]<N; k++) {                        if (k*k-a[j] <= 0) continue;                        if (vis[k*k-a[j]] && k*k != a[j]*2) {                            flag = 0; break;                        }                    }                    if ( !flag ) break;                    dvis[a[j]] = 1;                }            }            else {//之前没有出现过                 for (int k=1; k*k-a[j] < N; k++) {                    if (k*k-a[j] <= 0) continue;                    if ( vis[k*k-a[j]] ) {//处理并查集                         if ( dvis[k*k-a[j]] || dvis[a[j]] ) { flag = 0; break;}                         if ( check(k*k-a[j], a[j]) ) {flag = 0; break;}                    }                 }                if ( !flag ) break;                vis[a[j]] = 1;            }        }        if ( !j ) break;        ans[++idc] = j;        //for ( ; i>j; i--) f[a[i]] = f[a[i] + N] = -1, vis[a[i]] = 0, dvis[a[i]] = 0;        for ( ; i>j; i--) f[a[i]] = a[i], f[a[i] + N] = a[i] + N, vis[a[i]] = 0, dvis[a[i]] = 0;    }}int main() {    freopen("division.in", "r", stdin);    freopen("division.out", "w", stdout);    scanf("%d%d", &n, &K);    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);    if (K == 1) solve1();    else solve2();    printf("%d\n", idc + 1);    for(int i=idc; i; i--) printf("%d ", ans[i]);    return 0;}
原创粉丝点击