2018 China Collegiate Programming Contest Final (CCPC-Final 2018) 题目链接
这道题的思维当真是一步一步的去接近答案,想了许久。
一、种类并查集划分关系进集合
首先,看到那个矛盾,第一想到的就是种类并查集,当然,因为这道题不是强制在线,所以当然也是可以直接用复杂度O(N)的二分图来完成这个处理。我们将这么多互相有关系的点,划分成了多个集合,每个集合都存在着一个(因为集合中只有一个点)、或者两个阵营。
其次,不难想到,把每个并查集的最大值和最小值处理出来,但是因为是哪个阵营还不能确定,假如跟节点选择0号阵营,那么就是其他各点都是对应的阵营;假如根节点选择1号阵营,那么其余各节点就是反选,如此维护出来每个并查集2种状态分别的最大和最小值。
二、枚举最小值(最大值也是可以的)
在处理完这个并查集之后,我们把问题拆解出来(分而治之),我们可以先看作对于一群集合,我们知道最大值和最小值(是每个集合的最大和最小值),现在对这么一群无序的区间段,我们很难去操作,但是假如对其中一维变得有序,就可以去查另一维了,所以,这里的处理方式是对于每个集合的最小值,我们按照最小值降序排序。
然后怎么去找到这样的答案呢?那么就是需要去控制最大值的问题了,我们把最大值放进数据结构中去维护一下,我们一一遍历最小值,直到有足够的点放进去的时候,就可以计算答案了,答案就是此时数据结构中的“最大值”的最大值,再去减去此时枚举到的最小值。
这里面的“最大值”指的是对应集合(因为实际中是有两个等集合的)中的{最小值,最大值},并且假如这两个都要放进数据结构的话,我们去取更小的,因为这样会更接近下限,使得答案更小。
三、数据结构维护最值(线段树)
这里只需要去维护一下最大值即可,我们把上面处理出来的,维护最大的“最大值”即可。
总的捋一下:
- 先把关系对立的进行处理,我用了种类并查集,然后放入集合中去即可;
- 根据每个并查集的阵营,我们罗列0、1阵营,分放不同集合;
- 现在处理出来两倍于并查集数量的集合群;
- 对于所有的集合,我们按照最小值降序排列;
- 从前往后枚举最小值,然后把其最大值放入数据结构中维护;
- 如果是相同并查集中的集合,我们放入数据结构中的,应当是最小的“最大值”;
- 如果是不同并查集中的集合,我们记录数量,当放进去的数量等同于并查集的数量的时候,就需要计算答案了(都要算)。
My Code:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7, IIF = 2e9 + 7;
int N, M, root[maxN], team[maxN], cnt, vis[maxN], _UP;
struct Point
{
int x, y;
Point(int a=0, int b=0):x(a), y(b) {}
}a[maxN];
int fid(int x)
{
if(x == root[x]) return x;
int tmp = root[x];
root[x] = fid(root[x]);
team[x] = (team[x] + team[tmp]) & 1;
return root[x];
}
struct node //0是原态,1是翻转态
{
int minn, maxx, id;
node(int a=INF, int b=-INF, int c=0):minn(a), maxx(b), id(c) {}
friend bool operator < (node e1, node e2) { return e1.minn == e2.minn ? e1.maxx < e2.maxx : e1.minn > e2.minn; }
}t[maxN<<1];
int tree[maxN<<2], in_tree[maxN], in_maxx[maxN];
void buildTree(int rt, int l, int r)
{
tree[rt] = -INF;
if(l == r) return;
int mid = HalF;
buildTree(Lson); buildTree(Rson);
}
inline void pushup(int rt) { tree[rt] = max(tree[lsn], tree[rsn]); }
void update(int rt, int l, int r, int qx, int val)
{
if(l == r) { tree[rt] = val; return; }
int mid = HalF;
if(qx <= mid) update(Lson, qx, val);
else update(Rson, qx, val);
pushup(rt);
}
inline void init()
{
cnt = _UP = 0;
for(int i=1; i<=N; i++)
{
root[i] = i;
team[i] = 0;
vis[i] = 0;
}
}
int main()
{
int T; scanf("%d", &T);
for(int Cas=1; Cas<=T; Cas++)
{
scanf("%d%d", &N, &M);
init();
bool flag = true;
for(int i=1, u, v, fa_u, fa_v; i<=M; i++)
{
scanf("%d%d", &u, &v);
if(!flag) continue;
fa_u = fid(u); fa_v = fid(v);
if(fa_u != fa_v)
{
root[fa_v] = fa_u;
team[fa_v] = (team[u] + 1 - team[v]) & 1;
}
else if(team[u] == team[v]) flag = false;
}
for(int i=1, u; i<=N; i++)
{
scanf("%d%d", &a[i].x, &a[i].y);
if(!flag) continue;
u = fid(i);
if(!vis[u])
{
cnt += 2;
vis[u] = cnt - 1;
if(!team[i])
{
t[cnt - 1] = node(a[i].x, a[i].x, ++_UP);
t[cnt] = node(a[i].y, a[i].y, _UP);
}
else
{
t[cnt - 1] = node(a[i].y, a[i].y, ++_UP);
t[cnt] = node(a[i].x, a[i].x, _UP);
}
in_tree[_UP] = false;
}
else
{
if(!team[i])
{
t[vis[u]].minn = min(t[vis[u]].minn, a[i].x);
t[vis[u]].maxx = max(t[vis[u]].maxx, a[i].x);
t[vis[u] + 1].minn = min(t[vis[u] + 1].minn, a[i].y);
t[vis[u] + 1].maxx = max(t[vis[u] + 1].maxx, a[i].y);
}
else
{
t[vis[u]].minn = min(t[vis[u]].minn, a[i].y);
t[vis[u]].maxx = max(t[vis[u]].maxx, a[i].y);
t[vis[u] + 1].minn = min(t[vis[u] + 1].minn, a[i].x);
t[vis[u] + 1].maxx = max(t[vis[u] + 1].maxx, a[i].x);
}
}
}
printf("Case %d: ", Cas);
if(!flag) { printf("IMPOSSIBLE\n"); continue; }
buildTree(1, 1, _UP);
sort(t + 1, t + cnt + 1);
int sum = 0, _id = 0, ans = IIF;
for(int i=1; i<=cnt; i++)
{
if(sum == _UP)
{
if(t[i].maxx < t[in_maxx[t[i].id]].maxx) update(1, 1, _UP, in_tree[t[i].id], t[i].maxx);
ans = min(ans, tree[1] - t[i].minn);
}
else
{
if(!in_tree[t[i].id])
{
sum++; in_tree[t[i].id] = ++_id; in_maxx[t[i].id] = i;
update(1, 1, _UP, _id, t[i].maxx);
if(sum == _UP) ans = min(ans, tree[1] - t[i].minn);
}
else if(t[i].maxx < t[in_maxx[t[i].id]].maxx) update(1, 1, _UP, in_tree[t[i].id], t[i].maxx);
}
}
printf("%d\n", ans);
}
return 0;
}