AtCoder Regular Contest 084F: XorShift

https://beta.atcoder.jp/contests/arc084/tasks/arc084_d

bitの扱いもうちょっとわかってたらもうちょっと早く思いついたかな。

すべての数がある数Pによって構成できることが証明できます。これときPはgcdに他なりません。
なのでgcdをユークリッドの互除法チックなことをして求めればいいです。

Pで構成できる数のうちX以下の数の個数を数え上げるのは簡単なDPでできます。しかし、場合分けをミスってWA。最後+1するかしないかでWAの2WA。

最初は線形代数っぽいし、rank求めるのが本質かなと思ったけど全然違った。

const int N = 4010;
ll pow2[4020];

vi bxor(const vi& a, const vi& b) {
	vi res(N, 0);
	rep(i, 0, N) res[i] = (a[i] ^ b[i]);
	return res;
}

int fbit(const vi& a) { //fbit(0101) = 3
	rer(i, N, 0) {
		if(a[i] == 1) return i + 1;
	}
	return 0;
}

vi shift(const vi& a, int k) {
	vi res(N, 0);
	int fa = fbit(a);
	assert(fa + k <= N);
	rep(i, 0, fa) {
		res[i + k] = a[i];
	}
	return res;
}

bool is_zero(const vi& a) {
	rep(i, 0, N) {
		if(a[i]) return false;
	}
	return true;
}

vi gcd(const vi& a, const vi& b) {
	if(is_zero(b)) return a;
	vi c = a;
	while(true) {
		int fc = fbit(c), fb = fbit(b);
		if(fc < fb) break;
		int diff = fc - fb;
		c = bxor(c, shift(b, diff));
	}
	return gcd(b, c);
}

vi stobit(const string& str) {
	vector<int> res(N, 0);
	int n = sz(str);
	rep(i, 0, n) {
		res[n - i - 1] = str[i] - '0';
	}
	return res;
}

int M;
vi X;
vi B[10];

void solve() {
	string str;
	cin >> M >> str;
	pow2[0] = 1;
	rep(i, 1, 4010) pow2[i] = pow2[i - 1] * 2 % mod;
	X = stobit(str);
	rep(i, 0, M) {
		cin >> str;
		B[i] = stobit(str);
	}
	vi g = B[0];
	rep(i, 0, M - 1) g = gcd(g, B[i + 1]);
	int a = fbit(X), b = fbit(g);
	vi c(N, 0);
	ll res = 0;
	rep(i, 0, (a - b + 1)) {
		if(X[a - i - 1] == 1) {
			ADD(res, pow2[a - b - i]);
			if(c[a - i - 1] == 0) c = bxor(c, shift(g, (a - b - i)));
		}
		if(X[a - i - 1] == 0 && c[a - i - 1] == 1) {
			c = bxor(c, shift(g, (a - b - i)));
		}
	}
	rer(i, N, 0) {
		if(X[i] == 0 && c[i] == 1) {
			cout << res << "\n";
			return;
		}
		else if(X[i] == 1 && c[i] == 0) {
			cout << (res + 1) % mod << "\n";
			return;
		}
	}
	cout << (res + 1) % mod << "\n";
}

bitの問題にもっと強くなりたい。

SRM 641

https://community.topcoder.com/stat?c=round_overview&er=5&rd=16084

Easy
原点と2つの頂点がつくる角度a,b,cについて、
a+b+c=2*π,a<π,b<π,c<πが成り立っていればいいです。
よって2つ頂点を決めれば最後の頂点は2*π-a-b < c < πを満たせばよくて、これは前処理をしておけば二分探索で求めることができます。O(N^2logN)(N=2500)でめちゃくちゃ重いけどギリギリ通ったw
本当はx軸からの角度でsortして解くのが正しくて、そうすればO(N^2)になりますね。

struct TrianglesContainOrigin {
  vector<int> x;
  vector<int> y;
  vector<pair<double, int> > P[2510];
  long long count(vector<int> _x, vector<int> _y) {
    x = _x, y = _y;
	int n = sz(x);
	rep(i, 0, n) {
		rep(j, 0, n) {
			if(i == j) continue;
			comp b = comp(x[j], y[j]), a = comp(x[i], y[i]);
			double ag = arg(b / a);
			if(ag < 0) continue;
			P[i].pb(make_pair(ag, j));
		}
		sort(all(P[i]));
	}
	ll res = 0;
	rep(i, 0, n) {
		rep(j, 0, sz(P[i])) {
			double arg = P[i][j].fst;
			int at = P[i][j].sec;
			int tmp = upper_bound(all(P[at]), make_pair(PI - arg, -1)) - P[at].begin();
			res += sz(P[at]) - tmp;
		}
	}
	return res / 3;
  }
};

211 minutes 4 secs
普通に全然思いつかなかったんですが…。幾何全然手をつけてないのもある。

Medium
操作を逆から見ます。例えば6,3,5,2,4,1について考えると、
まず6,5,4と3,2,1に分けて、2つを好きなように並び替えてそのままくっつける作業になります。
なので5,4,6,1,3,2や4,5,6,1,2,3などにできます。すると2つ分けたあとは好きに入れ替えることができるので、nより大きいか小さいかだけが重要になります。よって奇数番目に位置しているn以下の数をa個として(例えば上の例だと6,5,4なのでa=0)次に何個の状態に移動できるか考えます。そうしてできたグラフ上でダイクストラすればいいです。と思ったけど辺の長さが全部同じなので幅優先してください。

struct ShufflingCardsDiv1 {
  vector<int> permutation;
  vector<int> G[2010];
  int d[2010];

  int shuffle(vector<int> _permutation) {
    permutation = _permutation;
	int n2 = sz(permutation), n = n2 / 2;
	bool found = false;
	rep(i, 0, n2) {
		if(permutation[i] != i + 1) found = true;
	}
	int cnt = 0;
	rep(i, 0, n2) 
		if(permutation[i] <= n) cnt += ((i + 1) % 2);
	if(!found) return 0;
	rep(i, 0, n + 1) {
		int j = n - i;
		int minx = max(n / 2 - i, 0), maxx = min((n + 1) / 2, i);
		int miny = max((n + 1) / 2 - j, 0), maxy = min(n / 2, j);
		for(int k = minx + miny; k <= maxx + maxy; k++) {
			G[i].pb(k);
		}
	}
	fill(d, d + n + 1, inf);
	d[cnt] = 0;
	priority_queue<pi, vector<pi>, greater<pi>> que;
	que.push(pi(0, cnt));
	while(!que.empty()) {
		pi p = que.top(); que.pop();
		int td = p.fst, v = p.sec;
		if(td > d[v]) continue;
		rep(i, 0, sz(G[v])) {
			int to = G[v][i];
			if(d[v] + 1 < d[to]) {
				d[to] = d[v] + 1;
				que.push(pi(d[to], to));
			}
		}
	}
    return d[n] + 1;
  }
};

133 minutes 38 secs。これは難しい。

SZKOpuł: Leonardo's Numbers

https://szkopul.edu.pl/problemset/problem/yGC3v6-xidN3ZW6QNlBAFZSU/site/?key=statement

L(i+1)^x * L(i)^yを求める際に必要な情報を考えてみます。これを(x,y)と表記します。
L(i+1)^x = (L(i) + L(i - 1) + 1) ^ x = Σ(a+b+c <= x)(x!/a!b!c!)L(i)^a * L(i)^b より
L(i+1)^x * L(i)^y = Σ(a+b+c <= x)(k!/a!b!c!)L(i)^(a+y) * L(i)^bとなります。
整理すると、(x,y)を求めるためには(a+y,b)(a+b <= x)が必要となり、a+y=X,b=Yとおくと、XY平面上でY<=x && y<=X<=x+y-Yを満たす格子点に相当する点について情報があればいいです。
上のXYの条件は図示すると(y,x),(y,0),(x+y,0)を頂点とする二等辺三角形の領域と対応し、よってX+Y<=kを満たすような(X,Y)(すなわちL(i)^X*L(i-1)^Y)について情報を持っておけば、(x,y)(すなわちL(i+1)^x*L(i)^Y)についてもx+y<=kをみたす領域について求めることができます。なので行列は(x,y)の情報((k+1)*(k+2)/2)個と、I(n)=Σ(i<=n)L(i)についての情報1個の計M=((k+1)*(k+2)/2+1)個持って累乗すればI(n)を求められます。k<=13のときM=106となるのでO(M^3 * logN)でなんとか間に合います。

M*2とかだとTLEします。A+A^2+A^3...を求める関数が用意してあったので、最初脳死でI(n)をこれを使って求めましたが、行列のサイズがM*2となるので見事にTLEしました。数列の和を求める際は普通に1つだけ要素を増やすだけでいいですね。行列の形にできないと思ったら1つ要素を追加することを考えるのが良さそうです。

void show(ll a) {
	vi vec;
	rep(i, 0, 9) {
		vec.pb(a % 10); a /= 10;
	}
	rer(i, 9, 0) cout << vec[i];
	cout << endl;
}

ull N;
int K;
ll fac[20];
int id[200][200];

void solve() {
	cin >> N >> K;
	fac[0] = fac[1] = 1;
	rep(i, 2, K + 1) fac[i] = fac[i - 1] * i;
	int M = (K + 1) * (K + 2) / 2 + 1;
	mat A(M, vl(M, 0));
	int cnt = 0;
	rep(x, 0, K + 1) {
		rep(y, 0, K + 1) {
			if(x + y > K) continue;
			id[x][y] = cnt; cnt++;
		}
	}
	rep(x, 0, K + 1) {
		rep(y, 0, K + 1) {
			if(x + y > K) continue;
			int pid = id[x][y];
			rep(a, 0, x + 1) {
				rep(b, 0, x + 1) {
					int c = x - a - b;
					if(c < 0) continue;
					ll mul = fac[x] / (fac[a] * fac[b] * fac[c]);
					int tid = id[a + y][b];
					A[pid][tid] = mul;
				}
			}
		}
	}
	int x = K, y = 0;
	rep(a, 0, x + 1) {
		rep(b, 0, x + 1) {
			int c = x - a - b;
			if(c < 0) continue;
			ll mul = fac[x] / (fac[a] * fac[b] * fac[c]);
			int tid = id[a + y][b];
			A[M - 1][tid] = mul;
			A[M - 1][M - 1] = 1;
		}
	}
	mat B = mat_pow(A, N - 1);
	ll res = 0;
	rep(i, 0, M) {
		if(i != M - 1) ADD(res, B[M - 1][i]);
		else ADD(res, B[M - 1][i] * 2 % mod);
	}
	show(res);
}

uLLにしないといけないのもさりげない罠。

AOJ 2107: Can I go there?

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2107

頂点を行列にするとうまく行かなくて、辺を行列にするといい。すなわち
ある辺のうちどっちにいるかを値にして、次どこ行けるのか行列で持てば良い。
最初の1回はどこにもいけるので特別な辺を貼って対処する。

ほとんどデバッグしないで通ったのでいいね。

int N, M, Z;
int E[60][2];

void solve_sub() {
	mat A(M * 2, vl(M * 2, 0));
	rep(i, 0, M) {
		rep(j, 0, 2) {
			int v = E[i][j];
			int id = i * 2 + j;
			rep(k, 0, M) {
				if(i == k) continue;
				if(E[k][0] == v) {
					int tid = k * 2 + 1;
					A[tid][id] = 1;
				}
				else if(E[k][1] == v) {
					int tid = k * 2;
					A[tid][id] = 1;
				}
			}
		}
	}		
	//rep(i, 0, sz(A)) debug(A[i]);
	mat B = mat_pow(A, Z);
	rep(i, 0, M) {
		rep(j, 0, 2) {
			int id = i * 2 + j;
			if(E[i][j] == N - 1) {
				if(B[id][2 * M - 2]) {
					cout << "yes\n";
					return;
				}
			}
		}
	}		
	cout << "no\n";
}
void solve() {
	while(true) {
		cin >> N >> M >> Z;
		if(N == 0 && M == 0 && Z == 0) return;
		memset(E, 0, sizeof(E));
		rep(i, 0, M) {
			int a, b;
			cin >> a >> b; a--; b--;
			E[i][0] = a; E[i][1] = b;
		}
		E[M][0] = 0; E[M][1] = N + 1;
		M++;
		solve_sub();
	}
}

SRM 640

https://community.topcoder.com/stat?c=round_overview&er=5&rd=16083

Easy
Kruskalやるだけなんだけど貼るの遅かった…もうちょっとちゃんと整備します。

struct ChristmasTreeDecoration {
  vector<int> col;
  vector<int> x;
  vector<int> y;
  int solve(vector<int> _col, vector<int> _x, vector<int> _y) {
    col = _col, x = _x, y = _y;
	N = sz(col);
	init(N);
	E = 0;
	rep(i, 0, sz(x)) {
		int a = x[i] - 1, b = y[i] - 1;
		if(col[a] == col[b]) add_edge(a, b, 1);
		else add_edge(a, b, 0);
	}
    return kruskal();
  }
};

12 minutes 58 secs。

Medium
点の集合uとvの間にフローを流すことを考えます
uの部分集合u'と対応する集合v'をΓ(u')とします。
フローを流すと|u'|-|Γ(u')|が最大となるu'を求めることができます。(|u'|-|Γ(u')|=|u|-{|u-u'|+Γ(u')}で中カッコ内は最小カット問題にほかならないから)
逆に考えると、最大マッチングがcだとすると、|u'|-|Γ(u')| = a - cとなるので、
u'からは辺が|Γ(u')|=|u'|-a+c本引け、u - u'からはb本引けます。よって引ける辺は
|u'|^2 - (|u|+|v|-c)|u'| + |u||v|となります。また次数の条件より
|u| + d - c <= |u'| <= |u| - dを満たしている必要があります。あとは二次関数の最大値問題になるので解けます。

Hallの結婚定理とか残余グラフの話を少し忘れていましたね。

struct MaximumBipartiteMatchingProblem {
  int n1;
  int n2;
  int ans;
  int d;
  ll a, b, c;
  ll f(ll k) {
	  return k * k - (a + b - c) * k + a * b;
  }
  long long solve(int _n1, int _n2, int _ans, int _d) {
    n1 = _n1, n2 = _n2, ans = _ans, d = _d;
	a = n1; b = n2; c = ans;
	if(c == min(a, b)) return a * b;
	else if(a + d - c  <= a - d) {
		ll s1 = f(a + d - c);
		ll s2 = f(a - d);
		ll s3 = -1;
		if(a + d - c <= (a + b - c) / 2 && (a + b - c) / 2 <= a - d) {
			s3 = f((a + b - c) / 2);
		}
		if(a + d - c <= (a + b - c + 1) / 2 && (a + b - c + 1) / 2 <= a - d) {
			s3 = f((a + b - c + 1) / 2);
		}
		return max(max(s1, s2), s3);
	}
    else return -1;
  }
};

61 minutes 36 secs。でもいろいろ調べてやった割にはまあまあ速く通せたかな。

SRM 638

https://community.topcoder.com/stat?c=round_overview&er=5&rd=16081

Easy
まずブロックがないとわかっているところを除外します。
あっても良いブロックで連結成分が何個かできたとします。
この連結成分を同時に2個取ることはできません。また各連結成分でブロックを減らす意味はないので、連結成分ごとに条件を満たすか試せばよいです。

連結成分が1つであることが条件だと勘違いしてWA。ブロックがないときの場合分けでWA。300点だけあってコーナーケースを考慮するのも難しい。

struct ShadowSculpture {
  vector<string> XY;
  vector<string> YZ;
  vector<string> ZX;
  int n;
  bool block[11][11][11];
  bool used[11][11][11];
  bool connect[11][11][11];
  int dx[6] = {1, -1, 0, 0, 0, 0};
  int dy[6] = {0, 0, 1, -1, 0, 0};
  int dz[6] = {0, 0, 0, 0, 1, -1};

  void loop(int x, int y, int z) {
	  used[x][y][z] = true;
	  connect[x][y][z] = true;
	  rep(i, 0, 6) {
		  int nx = x + dx[i], ny = y + dy[i], nz = z + dz[i];
		  if(nx < 0 || n <= nx || ny < 0 || n <= ny || nz < 0 || n <= nz) continue;
		  if(!used[nx][ny][nz] && block[nx][ny][nz]) loop(nx, ny, nz);
	  }
  }

  bool checker() {
	  vector<string> txy = XY;
	  vector<string> tyz = YZ;
	  vector<string> tzx = ZX;
	  rep(i, 0, n) {
		  rep(j, 0, n) {
			  rep(k, 0, n) {
				  if(connect[i][j][k]) {
					  txy[i][j] = 'N';
					  tyz[j][k] = 'N';
					  tzx[k][i] = 'N';
				  }
			  }
		  }
	  }
	  rep(i, 0, n) 
		  rep(j, 0, n) {
			  if(txy[i][j] == 'Y' || tyz[i][j] == 'Y' || tzx[i][j] == 'Y') return false;
		  }
	  return true;
  }

  string possible(vector<string> _XY, vector<string> _YZ, vector<string> _ZX) {
    XY = _XY, YZ = _YZ, ZX = _ZX;
	n = sz(XY);
	rep(i, 0, n) {
		rep(j, 0, n) {
			rep(k, 0, n) block[i][j][k] = true;
		}
	}
	rep(i, 0, n) {
		rep(j, 0, n) {
			if(XY[i][j] == 'N') {
				rep(k, 0, n) {
					block[i][j][k] = false;
				}
			}
		}
	}
	rep(i, 0, n) {
		rep(j, 0, n) {
			if(YZ[i][j] == 'N') {
				rep(k, 0, n) {
					block[k][i][j] = false;
				}
			}
		}
	}
	rep(i, 0, n) {
		rep(j, 0, n) {
			if(ZX[i][j] == 'N') {
				rep(k, 0, n) {
					block[j][k][i] = false;
				}
			}
		}
	}
	bool found = false;
	bool found2 = false;
	rep(i, 0, n) {
		rep(j, 0, n) {
			if(XY[i][j] == 'Y' || YZ[i][j] == 'Y' || ZX[i][j] == 'Y') found2 = true;
			rep(k, 0, n) {
				if(block[i][j][k]) found = true;
			}
		}
	}
	if(!found) {
		if(!found2) return "Possible";
		else return "Impossible";
	}
	rep(i, 0, n) {
		rep(j, 0, n) {
			rep(k, 0, n) {
				if(block[i][j][k] && !used[i][j][k]) {
					memset(connect, 0, sizeof(connect));
					loop(i, j, k);
					if(checker()) return "Possible";
				}
			}
		}
	}
	return "Impossible";
  }
};

49 minutes 49 secs、2WAしたとしても遅すぎる。printデバッグする回数が減ればいいんだけど…

Medium
良問だと思う。
まず一番大きい要素をvとして、vよりも右側にあって、かつvとの和がmaxSizeSumを超えるものはvより左側にはいけません。
同じようにvより左側の要素についても言えます。しかしvとの和がmaxSizeSumより小さい要素は、permutationのどの順番にも来ていいので、配列を一番大きい要素で二分して再起的に解けばよいです。

struct NarrowPassage2 {
  vector<int> size;
  int maxSizeSum;
  ll loop(vector<int> vec) {
	  if(sz(vec) <= 1) return 1;
	  int n = sz(vec);
	  int at = -1, mv = -1;
	  rep(i, 0, n) {
		  if(vec[i] > mv) {
			  mv = vec[i];
			  at = i;
		  }
	  }
	  vector<int> lvec, rvec;
	  int cnt = 0;
	  rep(i, 0, at) {
		  if(vec[i] + vec[at] > maxSizeSum) {
			  lvec.pb(vec[i]);
		  }
		  else cnt++;
	  }
	  rep(i, at + 1, n) {
		  if(vec[i] + vec[at] > maxSizeSum) {
			  rvec.pb(vec[i]);
		  }
		  else cnt++;
	  }
	  ll ans = 1;
	  MUL(ans, loop(lvec));
	  MUL(ans, loop(rvec));
	  rep(i, 0, cnt) {
		  MUL(ans, n - i);
	  }
	  return ans;
  }
  int count(vector<int> _size, int _maxSizeSum) {
    size = _size, maxSizeSum = _maxSizeSum;
    return loop(size);
  }
};

トポロジカルソートのなんかだと思ったけど全然違って面白かった。
トポロジカルソートを数えるのはやっぱりO(N*2^N)かかりそう。
考察は永遠と時間を使ったけど、実装だけなら4 minutes 44 secs。

SRM 639

https://community.topcoder.com/stat?c=round_overview&er=5&rd=16082

Easy
なんも難しいことないのに題意勘違いして無限に時間溶かした。さらにオーバーフローもするしいいことなし。

struct AliceGame {
  long long x;
  long long y;
  long long findMinimumValue(long long _x, long long _y) {
    x = _x, y = _y;
	ll s = x + y;
	ll n = 0;
	for(int i = 1; i <= s; i += 2) {
		n++; s -= i;
	}
	if(s != 0) return -1;
	for(ll k = 0; k <= n; k++) {
		if(k * k <= x && x <= (2 * n - k) * k && (x - k) % 2 == 0) return k;
	}
    return -1;
  }
};

Medium
rowとcolumnに分けて考えることができます。
求めるものは(rowのみに並行に折った時の状態数)*(columnのみに並行に折った時の状態数)です。
対称性よりrowのみについて考えます。
紙が[l, r)をカバーする長方形になったとします。
次の状態としてありうるのは[l, i)をrの方向へ折った時と[i, r)をlの方向へ折った時の2通りです。
iをl < i < rを満たすものついてすべて試して、折れるかどうかはO(r - l)で調べられるので、メモ化して数えるとO(N^4)とかになりますが、折れるかどうかは前処理で計算できて、結局O(N^3)になります。

ちゃんと実装分割して考えればそんなバグらせないこともわかった。

struct BoardFolding {
  int N;
  int M;
  bool fold[300][300];
  bool ufold[300][300], lfold[300][300];
  bool dp[300][300];
  int tonumber(char c) {
	  if('0' <= c && c <= '9') return c - '0';
	  else if('a' <= c && c <= 'z') return c - 'a' + 10;
	  else if('A' <= c && c <= 'Z') return c - 'A' + 36;
	  else if(c == '#') return 62;
	  else return 63;
  }

  void loop(int l, int r) {
	  // debug(l, r);
	  if(dp[l][r] == true) return;
	  dp[l][r] = true;
	  for(int i = l + 1; i < r; i++) {
		  if(2 * i - l > r) continue;
		  if(ufold[l][i]) loop(i, r);
	  }
	  for(int i = l + 1; i < r; i++) {
		  if(2 * i - r < l) continue;
		  if(lfold[i][r]) loop(l, i);
	  }
  }
  int solve_sub(const vector<vi>& paper) {
	  int n = sz(paper), m = sz(paper[0]);
	  memset(fold, 0, sizeof(fold));
	  memset(ufold, 0, sizeof(ufold));
	  memset(lfold, 0, sizeof(lfold));
	  memset(dp, 0, sizeof(dp));
	  rep(i, 0, n) {
		  rep(j, 0, n) {
			  bool found = false;
			  rep(k, 0, m) {
				  if(paper[i][k] != paper[j][k]) found = true;
			  }
			  if(!found) fold[i][j] = true;
		  }
	  }
	  rep(i, 0, n) {
		  rep(j, i + 1, n) {
			  if(2 * j - i > n) continue;
			  bool found = false;
			  rep(k, 0, j - i) {
				  if(!fold[j - k - 1][j + k]) found = true;
			  }
			  if(!found) ufold[i][j] = true;
		  }
	  }
	  rep(i, 1, n + 1) {
		  rep(j, i + 1, n + 1) {
			  if(2 * i - j < 0) continue;
			  bool found = false;
			  rep(k, 0, j - i) {
				  if(!fold[i - k - 1][i + k]) found = true;
			  }
			  if(!found) lfold[i][j] = true;
		  }
	  }
	  // rep(i, 0, n + 1) 
		 //  rep(j, 0, n + 1) debug(i, j, fold[i][j], ufold[i][j], lfold[i][j]);
	  loop(0, n);
	  int res = 0;
	  rep(i, 0, n + 1) 
		  rep(j, 0, n + 1) {
			  // debug(i, j, dp[i][j]);
			  res += dp[i][j];
		  }
	  // debug(res);
	  return res;
  }
  vector<string> compressedPaper;
  int howMany(int _N, int _M, vector<string> _compressedPaper) {
    N = _N, M = _M, compressedPaper = _compressedPaper;
	vector<vi> paper(N, vi(M, 0));
	int ans = 1;
	rep(i, 0, N) 
		rep(j, 0, M) 
			paper[i][j] = (tonumber(compressedPaper[i][j / 6]) >> (j % 6)) % 2;
	ans *= solve_sub(paper);
	vector<vi> paper1(M, vi(N, 0));
	rep(i, 0, M) 
		rep(j, 0, N) 
			paper1[i][j] = paper[j][i];
	ans *= solve_sub(paper1);
	return ans;
  }
};