kmjp's blog

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

Codeforces ECR #188 : D. Alternating Path

悪くない出来。
https://codeforces.com/contest/2204/problem/D

問題

N点M辺の無向グラフが与えられる。
各辺に向きを付けたとき、ある点vから(元のグラフで)至る任意のパスにおける向きが交互となるようなvは最大何個できるか。

解法

連結成分毎に考える。
二部グラフを成す連結成分について、両側の頂点群のうち多い方を始点とするように向きをつければよい。

template<int um> class UF {
	public:
	vector<int> par,rank,cnt,G[um];
	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<404040> uf;
int T,N,M;
int tar[404040];
int num[404040];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>T;
	while(T--) {
		cin>>N>>M;
		uf.reinit(2*N);
		FOR(i,2*N) tar[i]=i,num[i]=0;
		FOR(i,N) num[2*i]=1;
		FOR(i,M) {
			cin>>x>>y;
			x--,y--;
			if(uf[2*x]!=uf[2*y+1]) {
				num[tar[uf[2*x]]]=num[tar[uf[2*y+1]]]=num[tar[uf[2*x]]]+num[tar[uf[2*y+1]]];
				num[tar[uf[2*y]]]=num[tar[uf[2*x+1]]]=num[tar[uf[2*y]]]+num[tar[uf[2*x+1]]];
				tar[uf[2*x]]=tar[uf[2*y+1]]=min(tar[uf[2*x]],tar[uf[2*y+1]]);
				tar[uf[2*y]]=tar[uf[2*x+1]]=min(tar[uf[2*y]],tar[uf[2*x+1]]);
				uf(2*x,2*y+1);
				uf(2*x+1,2*y);
			}
		}
		int ret=0;
		FOR(i,N) if(uf[2*i]!=uf[2*i+1]&&tar[uf[2*i]]==2*i) {
			ret+=max(num[2*i],uf.count(2*i)-num[2*i]);
		}
		cout<<ret<<endl;
	}
}

まとめ

これは割とすんなり解いてる。