kmjp's blog

競技プログラミング参加記です

Codeforces #819 : D. Edge Split

ちょっといまいちだった回。
https://codeforces.com/contest/1726/problem/D

問題

N頂点M辺のグラフが与えられる。MはN+2以下である。
辺を赤青で彩色したとき、赤辺だけ考えたときの連結成分の数と、青辺だけ考えたときの連結成分の数の和が最小となるようにしたい。
彩色例を求めよ。

解法

まず最小全域木を赤辺で塗ってしまおう。
残りを青辺で塗るわけだが、青辺は高々3本である。
この辺が閉路を成すと連結成分がN-2個であるが、閉路でないとN-3個にできる。

もし青辺が閉路を成す場合、他の部分を青辺にできるか総当たりでチェックしよう。

int T;
int N,M;
int U[202020],V[202020];

template<int um> class UF {
	public:
	vector<int> par,rank,cnt;
	UF() {par=rank=vector<int>(um,0); cnt=vector<int>(um,1); for(int i=0;i<um;i++) par[i]=i;}
	void reinit(int num=um) {int i; FOR(i,num) rank[i]=0,cnt[i]=1,par[i]=i;}
	int operator[](int x) {return (par[x]==x)?(x):(par[x] = operator[](par[x]));}
	int count(int x) { return cnt[operator[](x)];}
	int operator()(int x,int y) {
		if((x=operator[](x))==(y=operator[](y))) return x;
		cnt[y]=cnt[x]=cnt[x]+cnt[y];
		if(rank[x]>rank[y]) return par[x]=y;
		rank[x]+=rank[x]==rank[y]; return par[y]=x;
	}
};
UF<202020> uf;
int dep[202020];
vector<int> E[202020];

void dfs(int cur,int pre,int d) {
	dep[cur]=d;
	FORR(e,E[cur]) if(e!=pre) dfs(e,cur,d+1);
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>T;
	while(T--) {
		cin>>N>>M;
		string S;
		uf.reinit(N);
		vector<int> C;
		set<int> D;
		FOR(i,N) E[i].clear();
		FOR(i,M) {
			cin>>U[i]>>V[i];
			U[i]--,V[i]--;
			if(U[i]>V[i]) swap(U[i],V[i]);
			if(uf[U[i]]!=uf[V[i]]) {
				uf(U[i],V[i]);
				S+="0";
				E[U[i]].push_back(V[i]);
				E[V[i]].push_back(U[i]);
			}
			else {
				S+="1";
				C.push_back(i);
				D.insert(U[i]);
				D.insert(V[i]);
			}
		}
		if(C.size()==3&&D.size()==3) {
			dfs(0,0,0);
			x=*D.begin();
			FORR(v,D) if(dep[v]>dep[x]) x=v;
			FOR(i,M) if(S[i]=='0'&&((U[i]==x&&dep[V[i]]<dep[x])||(V[i]==x&&dep[U[i]]<dep[x]))) {
				FOR(j,3) if(U[C[j]]==x||V[C[j]]==x) {
					S[C[j]]='0';
					break;
				}
				assert(j<3);
				S[i]='1';
				break;
			}
			assert(i<M);
		}
		
		
		cout<<S<<endl;
	}
}

まとめ

結構手間取ったな。