kmjp's blog

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

yukicoder : No.3426 Mod K Graph Increments (Hard)

幸いこれは余り手間取らず解けた。
https://yukicoder.me/problems/no/3426

問題

N点M辺の連結無向グラフが与えられる。また、正整数Kが与えられる。
初期状態で各点の値A[v]=0である。

ここで、任意の辺(u,v)を選び、A[u]=(A[u]+1)%K、A[v]=(A[v]+1)%Kを同時に行うことを、任意回数行えるとする。
Aを入力で指定されたBに一致させられるか判定せよ。

解法

  • グラフが二部グラフの場合
    • グラフが連結なため、結局グラフの両サイドの任意の点の対の値を同時にインクリメントできる。
    • よって、両サイドの整数値の総和をKで割った余りが一致すれば、問題の条件を達成できる。
  • グラフが二部グラフではない場合
    • グラフが連結なため、結局グラフの任意の点の対の値を同時にインクリメントできる。
    • 言い換えれば、sum(A)を2ずつインクリメントできる。よって、Kが奇数であるかsum(B)が偶数かのどちらかを満たすならば問題の条件を達成できる。
int T;
int N,M,K;
vector<int> E[202020];
vector<pair<int,int>> L;
int B[202020],D[202020];

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<202020> uf;

void dfs(int cur,int pre,int d) {
	D[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>>K;
		FOR(i,N) E[i].clear();
		L.clear();
		uf.reinit(N);
		FOR(i,M) {
			cin>>x>>y;
			x--,y--;
			if(uf[x]!=uf[y]) {
				E[x].push_back(y);
				E[y].push_back(x);
				uf(x,y);
			}
			else {
				L.push_back({x,y});
			}
		}
		FOR(i,N) cin>>B[i];
		dfs(0,0,0);
		ll A[2]={};
		FOR(i,N) (A[D[i]]+=B[i])%=K;
		
		if(A[0]==A[1]) {
			cout<<"Yes"<<endl;
			continue;
		}
		int odde=0;
		FORR2(a,b,L) if(D[a]==D[b]) odde++;
		if(odde&&(K%2||(A[0]+A[1])%2==0)) {
			cout<<"Yes"<<endl;
		}
		else {
			cout<<"No"<<endl;
		}
		
		
	}
}

まとめ

これは★3でもいいかなと思った。