The 2018 ACM-ICPC Asia Jiaozuo Regional Contest C. Supreme Command
题目链接
http://codeforces.com/gym/102028/problem/C
题目大意
给你一个 $n$ 行 $n$ 列的棋盘,保证每行每列都有且仅有一个棋子。有四种操作,向四个方向移动所有棋子,如果无法移动则停留在原地。两种询问,问某个棋子当前的位置,和当前情况下有多少个重叠再一起的棋子对。
题目分析
注意到每一行每一列都只有一个棋子,则会产生一个很有意思的性质:所有的棋子如果出现合并的情况必定存在它的 $x$ 坐标等于所有棋子的最小值或最大值且 $y$ 坐标也是所有棋子的最小值或最大值。那么一共有四种情况。首先考虑如何维护所有棋子的坐标。由于所有棋子的初始行列坐标都是唯一对应的,因此可以快速将所有点按照行列坐标分别排序。分别维护所有点的行列坐标,每次将所有能移动到边界的行列都合并,记录合并到第几个点即可。由于在最小值和最大值中间的每一个值上都会有一个点,因此可以很轻松的维护点的坐标。再考虑维护重叠的点。由于所有的点一定是在四个角处才能合并因此只需要维护四种情况。而当所有点都合并成一个的时候,这四种情况会合并,维护即可。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 3e5 + 10;
int n, m, t;
pair <int, int> point[maxn];
int c[2][maxn];
int treen[2][maxn];
/*
x y
1 1 1
1 2 2
2 1 3
2 2 4
*/
int getn(int x, int y, int d){
int ret[4][4];
ret[1][1] = 1;
ret[1][2] = 2;
ret[2][1] = 3;
ret[2][2] = 4;
ret[3][1] = 1;
ret[3][2] = 2;
ret[1][3] = 1;
ret[2][3] = 3;
ret[3][3] = 1;
if(d){
return ret[y][x];
}else{
return ret[x][y];
}
}
struct unionset {
int fa[maxn];
long long son[maxn];
int cnt;
int find(int x){
if(fa[x] != x){
fa[x] = find(fa[x]);
}
return fa[x];
}
int join(int x, int y){
int ra = find(x);
int rb = find(y);
if(ra != rb){
fa[ra] = rb;
son[rb] += son[ra];
}
return 0;
}
long long getans(){
int i, j;
long long ret = 0;
for(i=n+1;i<=n+4;i++){
if(find(i) == i and son[i] > 1){
ret += son[i] * (son[i] - 1) / 2;
}
}
return ret;
}
int init(){
int i, j;
for(i=1;i<=n+4;i++){
fa[i] = i;
son[i] = (i <= n ? 1 : 0);
}
return 0;
}
};
unionset s;
struct sttree {
int mx, mn, delta;
int l, r;
int query(int x){
if(x <= l){
return mn;
}else if(x < r){
return delta + x;
}else{
return mx;
}
}
int update(int x, int d){
if(x > 0){
if(mx + x <= n){
delta += x;
mx += x;
mn += x;
}else{
if(mn + x >= n){
l = n;
r = 1;
mx = n;
mn = n;
if(c[d][1] != 3){
if(c[!d][1] != 3){
for(int i=1;i<=n;i++){
c[d][i] = 3;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(3, c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
}
}else{
for(int i=1;i<=n;i++){
c[d][i] = 3;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(3, c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
}
s.join(n + 1, n + 3), s.join(n + 2, n + 4);
s.join(n + 1, n + 2), s.join(n + 3, n + 4);
}
}
}else{
int p = r - (mx - (n - x));
int u = r;
delta += x;
mn += x;
mx = n;
r = p;
for(int i=p;i<u;i++){
c[d][i] = 2;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(2, c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
}
}
}
}else{
if(mn + x >= 1){
delta += x;
mx += x;
mn += x;
}else{
if(mx + x <= 1){
mx = 1;
mn = 1;
l = n;
r = 1;
if(c[d][1] != 3){
if(c[!d][1] != 3){
for(int i=1;i<=n;i++){
c[d][i] = 3;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(3, c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
}
}else{
for(int i=1;i<=n;i++){
c[d][i] = 3;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(3, c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
}
s.join(n + 1, n + 3), s.join(n + 2, n + 4);
s.join(n + 1, n + 2), s.join(n + 3, n + 4);
}
}
}else{
int p = l + ((1 - x) - mn);
int u = l;
delta += x;
mn = 1;
mx += x;
l = p;
for(int i=u+1;i<=p;i++){
c[d][i] = 1;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(1, c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
}
}
}
}
return 0;
}
int init(){
mx = n;
mn = 1;
l = 1;
r = n;
delta = 0;
return 0;
}
};
sttree treex;
sttree treey;
int read(){
int x = 0;
char ch = getchar();
while('0' > ch or ch > '9'){
ch = getchar();
}
while('0' <= ch and ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
int main(){
int i, j;
int x, y;
char op;
int d;
t = read();
while(t--){
n = read();
m = read();
s.init();
for(i=1;i<=n;i++){
x = read();
y = read();
point[i] = make_pair(x, y);
treen[0][x] = treen[1][y] = i;
c[0][i] = c[1][i] = 0;
}
c[0][1] = c[1][1] = 1;
c[0][n] = c[1][n] = 2;
d = 0,i = 1;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(c[d][i], c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
d = 0,i = n;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(c[d][i], c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
d = 1,i = 1;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(c[d][i], c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
d = 1,i = n;
if(c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)] != 0){
s.join(treen[d][i], n + getn(c[d][i], c[!d][(d ? point[treen[d][i]].first : point[treen[d][i]].second)], d));
}
treex.init();
treey.init();
while(m--){
scanf(" %c", &op);
if(op == '!'){
printf("%lld\n", s.getans());
}else{
x = read();
if(op == 'L'){
treey.update(-x, 1);
}else if(op == 'R'){
treey.update(x, 1);
}else if(op == 'U'){
treex.update(-x, 0);
}else if(op == 'D'){
treex.update(x, 0);
}else if(op == '?'){
printf("%d %d\n", treex.query(point[x].first), treey.query(point[x].second));
}
}
}
}
return 0;
}