kmjp's blog

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

yukicoder : No.498 ワープクリスタル (給料日編)

確かに今日のyukicoderは聞きなれない曲が多い気がする。
http://yukicoder.me/problems/no/498

問題

2次元のグリッド上で、K種類のクリスタルを使いワープしていくことを考える。
i番目のクリスタルを使うと、現在の位置から(X[i],Y[i])だけ移動した相対位置のグリッドに移動できる。
i番目のクリスタルはN[i]個あり、1個のクリスタルで1回移動できる。

クリスタルをいくつか利用し、(0,0)から(Gx,Gy)に移動する際、使用するクリスタルの並び順は何通りあるか。
なお、同種のクリスタルは互いに区別されない。

解法

K,N[i]が小さいので、まずは並び順を無視してクリスタルの利用回数を総当たりし、(0,0)→(Gx,Gy)に移動できるケースを求めよう。
各クリスタルの使用回数をM[i]とすると、それらの並び順は \displaystyle \frac{(\sum_i M_i)!}{M_1!M_2! \dots M_K!}通りである。

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

int GX,GY,K;
int X[10],Y[10],N[10];
vector<int> R;


void dfs(int id, int cx,int cy) {
	if(id==K) {
		if(cx!=GX || cy!=GY) return;
		
		int sum=0;
		FORR(r,R) sum+=r;
		ll t = fact[sum];
		FORR(r,R) t = t*factr[r]%mo;
		(ret = ret + t)%=mo;
		
	}
	else {
		int i;
		FOR(i,N[id]+1) {
			R.push_back(i);
			dfs(id+1,cx+X[id]*i,cy+Y[id]*i);
			R.pop_back();
		}
		
	}
}

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>>GX>>GY>>K;
	FOR(i,K) cin>>X[i]>>Y[i]>>N[i];
	
	dfs(0,0,0);
	
	cout<<ret<<endl;
}

まとめ

次回で500問になりそうね。