5.10 省选模拟赛 网格 平面图欧拉定理 并查集
作者:互联网
LINK:网格
搞了一下午 总算把这个平面欧拉定理给搞懂了。
大体上是欧拉定理 的一些定义难以理解。
关于证明我也不太会。题解写的那叫一个抽象 最终看std明白了题解的意思。
平面图欧拉定理:定义 V是点数 E为边数 F为区域数 C为连通块个数
那么存在 V+F-E=C+1;的关系。
其中 关于边数E的则认为是一个八联通的边 对于区域则需要百度一下 当然三角形也算是一个区域 (广大的UOJ群友就是牛.
对于前者 在定理中就是定义 在这道题中则容易想到边的关系显然包含斜着的边 后者同理。
考虑这道题的暴力 第一子任务是暴力bfs 不过遇到边界就加上一些特殊操作。
对于第二个子任务 这个我没实现 想到一个复杂度为n^3的东西 每次便利整张图 然后利用 dfs树的性质来判断区域。不过实现较麻烦 可能还通过不了。
对于第三个子任务 离线之后容易想到利用并查集判断联通性 每次加边判断连通块的增减性即可。
对于第四个子任务 用第三个就不太能写了 关键是整张图的连通块个数不能得到 且后续操作也完成不了。
code: score 35
const int MAXN = 100010, maxn = 210, N = 1001010, MAX = 1010;
;
const int dx[4] = { 0, 0, 1, -1 };
const int dy[4] = { 1, -1, 0, 0 };
int T, Q, flag1, ans, id, flag, flag2, root;
struct wy {
int x, y;
} t[MAXN];
int b[maxn][maxn], vis[MAX][MAX];
int f[N], mark[N], ans1[MAXN];
queue<pii> q;
inline void bfs(int s1, int s2) {
q.push(mk(s1, s2));
while (q.size()) {
int x = q.front().F;
int y = q.front().S;
q.pop();
for (int i = 0; i < 4; ++i) {
int xx = dx[i] + x;
int yy = dy[i] + y;
if (xx < 0 || yy<0 | xx> 200 || yy > 200) {
flag = 1;
continue;
}
if (vis[xx][yy])
continue;
if (b[xx][yy] == id)
continue;
b[xx][yy] = id;
q.push(mk(xx, yy));
}
}
}
inline void sol1() {
rep(1, Q, i) {
int x = t[i].x ^ (T * ans);
int y = t[i].y ^ (T * ans);
x += 100;
y += 100;
vis[x][y] = 1;
int cnt = 1;
++id;
rep(0, 200, i) {
rep(0, 200, j) {
if (vis[i][j])
continue;
if (b[i][j] == id)
continue;
flag = 0;
b[i][j] = id;
bfs(i, j);
if (!flag)
++cnt;
}
}
ans = cnt;
put(ans);
}
exit(0);
}
inline int getfather(int x) { return x == f[x] ? x : f[x] = getfather(f[x]); }
inline void merge(int x, int y) {
int xx = getfather(x);
int yy = getfather(y);
f[xx] = yy;
return;
}
inline void sol2() {
root = 1001001;
f[root] = root;
rep(1, Q, i) {
t[i].x += 500;
t[i].y += 500;
int x = t[i].x;
int y = t[i].y;
vis[x][y] = 1;
}
rep(0, 1000, i) rep(0, 1000, j) f[i * 1000 + j] = i * 1000 + j;
rep(0, 1000, i) rep(0, 1000, j) {
if (vis[i][j])
continue;
int id1 = i * 1000 + j;
rep(0, 3, k) {
int xx = dx[k] + i;
int yy = dy[k] + j;
if (xx < 0 || yy < 0 || xx > 1000 || yy > 1000)
merge(root, id1);
else {
if (vis[xx][yy])
continue;
merge(xx * 1000 + yy, id1);
}
}
}
rep(0, 1000, i) rep(0, 1000, j) {
if (vis[i][j])
continue;
int xx = getfather(i * 1000 + j);
if (!mark[xx])
++ans;
mark[xx] = 1;
}
fep(Q, 1, i) {
ans1[i] = ans;
int x = t[i].x;
int y = t[i].y;
int id1 = x * 1000 + y;
f[id1] = id1;
vis[x][y] = 0;
++ans;
rep(0, 3, k) {
int xx = dx[k] + x;
int yy = dy[k] + y;
if (xx < 0 || yy < 0 || xx > 1000 || yy > 1000) {
if (getfather(id1) != getfather(root))
--ans;
merge(id1, root);
} else {
if (vis[xx][yy])
continue;
if (getfather(id1) != getfather(xx * 1000 + yy))
--ans;
// cout<<getfather(xx*1000+yy)<<' '<<getfather(id1)<<endl;
merge(id1, xx * 1000 + yy);
}
}
// put(ans);
}
rep(1, Q, i) put(ans1[i]);
exit(0);
}
int main() {
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
get(Q);
get(T);
rep(1, Q, i) {
int get(x), get(y);
t[i] = (wy){ x, y };
if (abs(x) > 100 && abs(y) > 100)
flag1 = 1;
if (abs(x) > 500 && abs(y) > 500)
flag2 = 1;
}
if (!flag1)
sol1();
if (!flag2 && !T)
sol2();
return 0;
}
正解就是利用欧拉定理来做。
容易想到最后答案求得其实是F-实心区域个数。
而容易发现实心的区域个数一定是为三角形的区域。
所以 我们仍要多维护一个三角形区域个数 设为O.
V直接维护 E也直接维护 C利用并查集维护 关键是对于O的维护 加入一个点的时候 这个点会造成贡献无疑是和其8联通的正方形。
暴力枚举12个三角形就可以维护。
更简便的方法是 枚举每个三角形的右下角 然后再枚举右上方极小正方形来判断。
值得注意的是 可能加上之前的三角形区域了 先减掉之前的即可。
const int MAXN = 100010;
const int dx[8] = { -1, -1, -1, 0, 0, 1, 1, 1 };
const int dy[8] = { 0, -1, 1, 1, -1, -1, 0, 1 };
int Q, T;
int ans, f[MAXN];
int V, E, C, O;
map<ll, int> H;
inline int getfather(int x) { return x == f[x] ? x : f[x] = getfather(f[x]); }
inline ll ha(int x, int y) {
x += INF, y += INF;
return (ll)x << 31 | y;
}
inline int qy(int x, int y) {
int cnt = 0;
if (H.find(ha(x, y)) != H.end())
++cnt;
if (H.find(ha(x + 1, y)) != H.end())
++cnt;
if (H.find(ha(x, y + 1)) != H.end())
++cnt;
if (H.find(ha(x + 1, y + 1)) != H.end())
++cnt;
if (cnt == 3)
return 1;
if (cnt == 4)
return 3;
return 0;
}
int main() {
// freopen("1.in","r",stdin);
get(Q);
get(T);
rep(1, Q, i) {
int get(x) ^ (T * ans), get(y) ^ (T * ans);
ll ww = ha(x, y);
++V;
++C;
O -= qy(x, y);
O -= qy(x - 1, y);
O -= qy(x, y - 1);
O -= qy(x - 1, y - 1);
H[ww] = i;
f[i] = i;
O += qy(x, y);
O += qy(x - 1, y);
O += qy(x, y - 1);
O += qy(x - 1, y - 1);
for (int j = 0; j < 8; ++j) {
int xx = dx[j] + x;
int yy = dy[j] + y;
if (H.find(ha(xx, yy)) == H.end())
continue;
int ww = H[ha(xx, yy)];
int w1 = getfather(i);
int w2 = getfather(ww);
++E;
if (w1 != w2) {
f[w1] = w2;
--C;
}
}
put(ans = (E + C + 1 - V - O));
// cout<<E<<' '<<V<<' '<<C<<' '<<O<<'\n';
}
return 0;
}
标签:省选,rep,查集,xx,getfather,yy,int,5.10,1000 来源: https://www.cnblogs.com/chdy/p/12871067.html