原题直通车:HDU_4879 ZCC loves march
题意概述:在m*m(m<=10^18)的矩阵中有n(n<=10^5)个数(1~n),有k(k<=10^5)次操作,有以下两种操作:
1、将一个数i在同一行或同一列上移动d个单位
2、将与数i在同一行或同一列的所有数都移到i所在的位置,并输出所有(xi-xj)^2+(yi-yj)^2的和
分析:
1、因为只有移动和操作,所以占用到的坐标数最多为n+k个,所以用一个map将每个坐标映射到一个数。
2、对于操作2,为了便于查到每一行、每一列中有多少个数,所以用map映射到每一行、列分别一个set集合
3、放到每个set集合中的并不是n个数,而是每一个坐标的映射,因为每个坐标可能有多个数,但映射只有一个(理论上是),只要记录每个坐标上有多少个数即可,因为结果只与坐标有关,与n个数无关,以此避免TLE和MLE。
4、一个数只对应一个坐标,移动的时候,可以直接看成坐标的变化,也就是对应坐标映射的变化,所以只需用一个next数据记录移动的位置,数的数量直接相加即可。
5、虽然经过操作2之后,坐标中已经没有数了,但是后面可能会用到相应的next,所以经过了操作2的坐标要重新开辟一个映射,才不会和后面进行操作1时移动到这种坐标的数相应的next冲突。
附:2014 Multi-University Training Contest 2--by 镇海中学 解题报告
参考代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
using namespace std;
const int maxn = 1e5+100;
const int mod = 1e9+7;
int fa[maxn], num[maxn<<1], next[maxn<<1];
long long x[maxn<<1], y[maxn<<1];
typedef long long LL;
typedef pair<LL, LL> pl;
typedef map< pl, int > map_pl;
typedef map< LL, set<int> > map_LL;
typedef pair< map_LL::iterator, map_LL::iterator > pll;
map_LL mx, my;
map_pl M;
int n;
LL m, len, ans, d;
void Input() {
for(int i=1; i<=n; ++i) {
long long xx, yy;
scanf("%I64d%I64d", &xx, &yy);
pl p = make_pair(xx, yy);
if(M[p] == 0) {
x[len] = xx, y[len] = yy, num[len] = 0, next[len] = len;
pll px = mx.equal_range(xx), py = my.equal_range(yy);
if(px.first == px.second) { set<int>s; mx[xx] = s; } //还没有映射的集合
if(py.first == py.second) { set<int>s; my[yy] = s; }
mx[xx].insert(len), my[yy].insert(len);
M[p] = len++;
}
num[M[p]]++;
fa[i] = M[p];
}
}
int get_end(int rt) { //寻找最终的移动位置
return next[rt] == rt ? rt : (next[rt] = get_end(next[rt]));
}
int main() {
while(~scanf("%d%I64d", &n, &m)) {
len = 1, ans = 0;
mx.clear(), my.clear(), M.clear();
Input();
int k, o; scanf("%d", &k);
while(k--) {
char op[5];
scanf("%s%d", op, &o);
o ^= ans;
int rt = get_end(fa[o]);
fa[o] = rt; //指向移动的位置
pl p = make_pair(x[rt], y[rt]);
if(op[0] == 'Q') {
ans = 0;
set<int>sx = mx[x[rt]], sy = my[y[rt]]; //同行、同列的坐标
set<int>::iterator it;
for(it=sx.begin(); it!=sx.end(); ++it)
if((*it) != rt) {
int ne = get_end(*it);
long long u = (y[ne] - y[rt] + mod) % mod;
u = ( (u * u)%mod )*num[ne] %mod;
ans = (ans + u) % mod;
my[y[ne]].erase(ne);
num[rt] += num[ne], num[ne] = 0, next[ne] = rt;
M[make_pair(x[ne], y[ne])] = 0; // 对应的位置不能再用,因为可能有其它的next指向这里
}
for(it=sy.begin(); it!=sy.end(); ++it)
if((*it) != rt) {
int ne = get_end(*it);
long long u = (x[ne] - x[rt] + mod) % mod;
u = ( (u * u)%mod )*num[ne] %mod;
ans = (ans + u) % mod;
my[x[ne]].erase(ne);
num[rt] += num[ne], num[ne] = 0, next[ne] = rt;
M[make_pair(x[ne], y[ne])] = 0; // 对应的位置不能再用,因为可能有其它的next指向这里
}
printf("%I64d\n", ans);
continue;
}
scanf("%I64d", &d);
long long xx = x[rt], yy = y[rt];
if(op[0] == 'U') xx -= d;
if(op[0] == 'D') xx += d;
if(op[0] == 'L') yy -= d;
if(op[0] == 'R') yy += d;
num[rt]--;
p = make_pair(xx, yy);
if(M[p] == 0) {
x[len] = xx, y[len] = yy, num[len] = 0, next[len] = len;
M[p] = len++;
}
fa[o] = M[p]; //o被移动到M[p]坐标,直接改变坐标指向
num[M[p]]++;
}
}
return 0;
}