AOJ 2235 Graph Construction
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2235
クエリを平方分割する問題.平方分割した後にいらない頂点をつぶすのが超大変.
Union-Find treeでがんばってつぶす.
以前からあった辺が,今みている区間のなかで破壊されるとき,あらかじめ辺を追加しておくのを忘れたりした.
全ての場合を網羅するのはとても大変.
#include <cstdio> #include <algorithm> #include <vector> using namespace std; #define fst first #define snd second #define IDX(a,v) distance((a).begin(),lower_bound((a).begin(),(a).end(),v)) typedef pair<int,int> P; typedef pair<P,int> Pi; typedef pair<P, P> PP; const int MAXN=40000,MAXK=40000,ROOTK=200; const int INF=1<<29; int N,K,sqrtK; int qtype[MAXK]; P qv[MAXK]; vector<PP> qc; // PP( P(追加時間,削除時間), P(頂点,頂点) ) vector<Pi> qd; // Pi( P(頂点,頂点), 削除時間 ) int UFtree[MAXN]; void init(){ for(int i=0;i<N;i++){UFtree[i]=i;} } int find_root(int v){ if(UFtree[v] == v) return v; return UFtree[v] = find_root(UFtree[v]); } void marge(int v, int u){ int x, y; x = find_root(v); y = find_root(u); UFtree[x] = y; } int newG[2*ROOTK][2*ROOTK]; bool visited[2*ROOTK]; bool is_connect(int s, int g, int vn){ visited[s] = true; if(s == g) return true; for(int i = 0; i < vn; i++){ if(newG[s][i] && !visited[i] && is_connect(i, g, vn)){ return true; } } return false; } int main(){ scanf("%d%d",&N,&K); for(int i=0;i<K;i++){ int t, u, v; scanf("%d%d%d", &t,&u,&v); qtype[i] = t; qv[i] = P(u,v); if(t == 1){ //追加される辺 qc.push_back(PP(P(i,INF),P(u,v))); }else if(t == 2){ //削除される辺 qd.push_back(Pi(P(u,v),i)); } } //追加される辺の削除時刻をもとめる sort(qd.begin(), qd.end()); bool used[qd.size()]; fill(used, used + qd.size(), false); for(int i=0; i < qc.size();i++){ P p = qc[i].snd; for(int k = IDX(qd, Pi(p,0)); k < qd.size() && qd[k].fst == p; k++){ //既に追加するものとペアになっている削除する辺をとばす if(!used[k]){ qc[i].fst.snd = qd[k].snd; used[k] = true; break; } } } //sqrtKを求める for(sqrtK=1; sqrtK * sqrtK < K; sqrtK++){ ; } //sqrtKずつクエリを処理 for(int i=0; i<K; i+=sqrtK){ //union find で関係ない頂点をつぶす init(); for(int j=0; j<qc.size(); j++){ if(qc[j].fst.fst < i && i+sqrtK <= qc[j].fst.snd){ //辺qc[j].sndは常に存在するので2つの頂点をmarge int v = qc[j].snd.fst, u = qc[j].snd.snd; marge(v, u); } } //範囲内で出現する頂点番号を記録する また連結をきく質問があるかみる bool use[N], f = false; fill(use,use+N,false); for(int j = 0; j<sqrtK && i+j<K; j++){ use[find_root(qv[i+j].fst)] = true; use[find_root(qv[i+j].snd)] = true; if(qtype[i+j] == 3) f = true; } if(!f) continue; //連結をきく質問がないなら計算する必要はない //頂点の番号を付け替える vector<int> newV; for(int j = 0; j < N; j++){ if(j == UFtree[j] && use[j]) newV.push_back(j); } //関係する頂点数はO(sqrt(K))である //よってdfsをすれば計算量はO(sqrt(K)) //グラフを初期化 for(int v=0; v<newV.size(); v++) for(int u=0; u<newV.size(); u++) newG[v][u] = 0; //以前からある辺を追加しておく for(int j=0;j<qc.size();j++){ if(qc[j].fst.fst < i && i <= qc[j].fst.snd && qc[j].fst.snd < i+sqrtK){ int x = find_root(qc[j].snd.fst), y = find_root(qc[j].snd.snd); int v = IDX(newV, x), u = IDX(newV, y); if(0<=v&&v<newV.size() && 0<=u&&u<newV.size()){ newG[v][u]++; newG[u][v]++; } } } //各クエリを処理 for(int j=0; j<sqrtK && i+j<K; j++){ //時刻i+jを考える int x = find_root(qv[i+j].fst), y = find_root(qv[i+j].snd); int v = IDX(newV,x), u = IDX(newV,y); if(qtype[i+j] == 3){ //連結であるかのチェック fill(visited, visited+newV.size(), false); if(is_connect(v, u, newV.size())){ puts("YES"); }else{ puts("NO"); } }else if(qtype[i+j] == 1){ //辺の追加 newG[v][u]++; newG[u][v]++; }else{ //辺の削除 newG[v][u]--; newG[u][v]--; } } } }