kmjp's blog

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

AtCoder ARC #165 : E - Random Isolation

この問題あんまり記憶にないな…。
https://atcoder.jp/contests/arc165/tasks/arc165_e

問題

N頂点の木と、正整数Kが与えられる。
K+1以上のサイズの連結成分がある限り、その連結成分の頂点を等確率で1つ選び、その頂点及びその頂点を端点とする辺を削除することを繰り返す。
操作回数の期待値を求めよ。

解法

1~NのPermutation Pに対し、その順で順次「点P[i]が属する連結成分がK+1以上のサイズなら、P[i]を消す」という風に読み替える。
各頂点vに対し、頂点vより先に他の頂点が消される結果、頂点vを消す順番が来たときに、サイズがK以下かどうか、を判定したい。
そこでvを根としてDFSを行い、SubTree内での消した頂点数と親方向と連結する残った頂点数に対し、その状態に至る組み合わせを数え上げて行こう。

int N,K;
vector<int> E[101];
const ll mo=998244353;

const int NUM_=2000003;
static ll fact[NUM_+1],factr[NUM_+1],inv[NUM_+1];


unordered_map<int,ll> dp[101];
ll ret;
void dfs(int cur,int pre) {
	unordered_map<int,ll> F;
	F[1]=1;
	
	FORR(e,E[cur]) if(e!=pre) {
		dfs(e,cur);
		unordered_map<int,ll> T;
		FORR2(a,b,F) {
			FORR2(a2,b2,dp[e]) {
				(T[a+a2]+=b*b2)%=mo;
			}
		}
		swap(F,T);
	}
	
	if(cur!=pre) {
		F[1000]=1;
		dp[cur]=F;
	}
	else {
		FORR2(a,b,F) {
			int aa=a/1000;
			int ab=a%1000;
			
			if(ab>K) {
				 (ret+=factr[aa+ab]*fact[ab-1]%mo*fact[aa]%mo*b)%=mo;
			}
		}
	}
	
	
}


void solve() {
	int i,j,k,l,r,x,y; string s;
	
	inv[1]=fact[0]=factr[0]=1;
	for (int i=2;i<=NUM_;++i) inv[i] = inv[mo % i] * (mo - mo / i) % mo;
	for (int i=1;i<=NUM_;++i) fact[i]=fact[i-1]*i%mo, factr[i]=factr[i-1]*inv[i]%mo;
	
	cin>>N>>K;
	FOR(i,N-1) {
		cin>>x>>y;
		E[x-1].push_back(y-1);
		E[y-1].push_back(x-1);
	}
	FOR(i,N) {
		dfs(i,i);
	}
	cout<<ret%mo<<endl;
}

まとめ

落ち着いてみると700ptぐらいの難易度かも。