SRM532 Div1(450) DengklekBuildingRoads
K≦8という制限がある。先頭の家から順に、自分より小さいに家との間にのみ道路を繋ぐとすると、直前K個の街の道路数の偶奇ごとに覚えておけば良い。道路を繋ぐ前のK個の家の道路の偶奇と、道路を繋いだ後の偶奇を決めると、それぞれの家に偶数本奇数本どちらの道路を繋ぐかが決まる。奇数本の道路を繋ぐ家にまず1本道路を繋いで、あとx本の道路をy個の家に繋ぐとすると、その方法はx/2+yCy。
時間計算量はO(NM222K)でギリギリ。最大ケースで1.8秒。ある家から、直前K個の家に一気に道路を繋ぐのではなく、遠い家から順に繋いでいき、何個前の家まで繋いだかごとにもメモすると、高速化できるし、プログラムも簡単になるらしい。
#include <string> #include <vector> using namespace std; int countbit( int n ) { n = ( n & 0x55555555 ) + ( n >> 1 & 0x55555555 ); n = ( n & 0x33333333 ) + ( n >> 2 & 0x33333333 ); n = ( n & 0x0f0f0f0f ) + ( n >> 4 & 0x0f0f0f0f ); n = ( n & 0x00ff00ff ) + ( n >> 8 & 0x00ff00ff ); n = ( n & 0x0000ffff ) + ( n >>16 & 0x0000ffff ); return n; } long long MOD = 1000000007LL; long long C[100][128]; vector<vector<vector<int> > > memo; int N, M, K; // n: 注目している家 // m: 残りの道路数 // k: 直前K個の家の道路数の偶奇 int BT( int n, int m, int k ) { if ( n>=N ) return m==0 && k==0 ? 1 : 0; if ( memo[n][m][k]!=-1 ) return memo[n][m][k]; unsigned long long ans = 0; for ( int kk=0; kk<(1<<min(K-1,n)); kk++ ) { int c = countbit(kk^k); if ( c<=m ) { int t = min(K,n)-1; for ( int i=c; i<=m; i+=2 ) ans += C[(i-c)/2+t][t]*BT(n+1,m-i,kk<<1|c&1); ans %= MOD; } } return memo[n][m][k] = (int)ans; } class DengklekBuildingRoads{public: int numWays( int N, int M, int K ) { for ( int i=0; i<100; i++ ) { C[i][0] = C[i][i] = 1; for ( int j=1; j<i; j++ ) C[i][j] = (C[i-1][j-1]+C[i-1][j])%MOD; } ::N = N, ::M = M, ::K = K; memo = vector<vector<vector<int> > >( N, vector<vector<int> >( M+1, vector<int>(1<<K,-1) ) ); return BT( 1, M, 0 ); }};