kmjp's blog

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

yukicoder : No.3104 Simple Graph Problem

これはアプローチはすぐ思いつくが、実装はちょっとめんどい。
https://yukicoder.me/problems/no/3104

問題

連結無向グラフが与えられる。
各点vには整数値A[v]が設定されている。

辺と整数xを選び、辺の両端の点a,bに対し、A[a]とA[b]にxを加算して998244353で割る、という手順を任意に繰り返せるとする。
Aの全要素を空にできるか。できるなら、各辺に対するxの値を答えよ。

解法

まず全域木を作る。
その際、もしあと1辺加えると奇数長の閉路ができてしまう場合、その1辺の両端の長短を(u,v)として、点uを根とした根付き木を考える。
奇数長の閉路がない場合は、適当な点を根にしてよい。

木ができてしまえば、まず葉から順に値を0にするようにしていこう。
もし最終的に根頂点uの値A[u]も0なら、それでok。

もしA[u]が0でない場合、奇数長の閉路ができないなら解なし。
奇数長の閉路がある場合、A[u]=Wだったとして、まずA[u],A[v]に-W/2を加算し、A[u]=W/2、A[v]=-W/2という状態にしよう。
あとはまた先ほどの容量で、vから順に0にしていけば、最終的に全点の値が0になる。

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;
int N,M;
ll B[101010];
const ll mo=998244353;
int U[202020],V[202020];
int used[202020];
vector<int> E[202020];

map<pair<int,int>,int> Es;
int parity[202020];
int P[202020];

ll ret[202020];

void dfs(int cur,int pre,int p) {
	parity[cur]=p;
	FORR(e,E[cur]) if(e!=pre) dfs(e,cur,p^1);
}
void dfs2(int cur,int pre) {
	P[cur]=pre;
	FORR(e,E[cur]) if(e!=pre) {
		dfs2(e,cur);
		ll a=mo-B[e];
		(B[e]+=a)%=mo;
		(B[cur]+=a)%=mo;
		ret[Es[{e,cur}]]+=a;
	}
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>M;
	FOR(i,N) {
		cin>>B[i];
		B[i]=(mo-B[i])%mo;
	}
	FOR(i,M) {
		cin>>U[i]>>V[i];
		U[i]--,V[i]--;
		Es[{U[i],V[i]}]=i;
		Es[{V[i],U[i]}]=i;
		if(uf[U[i]]!=uf[V[i]]) {
			used[i]=1;
			uf(U[i],V[i]);
			E[U[i]].push_back(V[i]);
			E[V[i]].push_back(U[i]);
		}
	}
	dfs(0,0,0);
	int root=0,par=-1;
	FOR(i,M) if(parity[U[i]]==parity[V[i]]) root=U[i],par=V[i];
	dfs2(root,root);
	
	if(B[root]) {
		if(par==-1) {
			cout<<-1<<endl;
			return;
		}
		ll a=mo-(B[root]*(mo+1)/2)%mo;
		(B[root]+=a)%=mo;
		(B[par]+=a)%=mo;
		(ret[Es[{root,par}]]+=a)%=mo;
		while(root!=par) {
			x=P[par];
			ll a=mo-B[par];
			(B[par]+=a)%=mo;
			(B[x]+=a)%=mo;
			ret[Es[{par,x}]]+=a;
			par=x;
		}
	}
	FOR(i,N) assert(B[i]==0);
	FOR(i,M) cout<<(ret[i]%mo+mo)%mo<<" ";
	cout<<endl;
}

まとめ

もう少し短く書けないものかなぁ。