TopCoder SRM536 Div1Hard

問題

係数が mod 2 の世界で、N (高々 49)次の多項式の M (高々 1016) 乗の K 乗の係数を求めよ。

解法

まず、多項式の M 乗は結構大変なことになりうるので、大変なことにならない状態を考える。
多項式の 2 乗を考えると、mod 2 の世界では各項の指数が 2 倍になったものに変化することがわかる。そこで、M を 2 進展開することにより、O(log M) 個の、項が高々 N 個の多項式の積として表せることになる。
その多項式の積を、次数が低い順に並べる。K 乗の項の係数を求めるためには、Σ(最初の多項式の i 乗の係数) * (それ以外の多項式の積の K-i 乗の係数) を求めればいい。
そのために set を用いて、求めるべき次数を管理する。まず set には K が入っている。最初の多項式から順に、「set の各要素 i について、(i - 各項の次数) としたものを次の set に追加する」ということを行う。この set は、2 個同じものを入れるとどちらも消えるようにしておく。これを最後の多項式まで行い、set に 0 が入っているか確認すればよい。
しかし、これでは単純に展開しても同じことである。だが、各多項式ごとに、各項の次数は、2 の冪の倍数になっていることを使うと、最初の set の追加のときに、次以降の多項式ではどう頑張っても絶対に 0 にできないようなものを排除することができ、これを行うと非常に速くなり、解くことができる。
(これだけで速くなる理由は、「すべて、ある 2 の冪の倍数」になっているなら情報は下位ビットに共通する 0 の数だけ失われていることになり、多項式を 1 個進めるごとに、「set の中にある次数」の差の情報が 1 ビット以上失われていき、差は高々 N しか拡大しないため、set の中身の差は高々 2N に抑えられることからわかる)
下の実装では、進めるごとに set の中身を 2 で割っている。

コード

#include<vector>
#include<algorithm>
#include<set>
using namespace std;
typedef long long i64;

class BinaryPolynomialDivOne
{
public:
	int findCoefficient(vector<int> A, i64 M, i64 K)
	{
		set<i64> prev, now;
		prev.insert(K);
		for(int i=0;i<60;i++){
			//succ
			if(M & (1LL << (i64)i)){
				for(set<i64>::iterator iter=prev.begin(); iter != prev.end(); iter++){
					for(int j=0;j<A.size();j++) if(A[j]){
						i64 nex = *iter - j;
						if(nex<0) continue;
						if(now.count(nex)==1){
							now.erase(nex);
						} else now.insert(nex);
					}
				}
				prev.swap(now);
				now.clear();
			}
			//right shift
			for(set<i64>::iterator iter=prev.begin(); iter != prev.end(); iter++){
				if(*iter%2==0) now.insert(*iter / 2);
			}
			prev.swap(now);
			now.clear();
		}
		
		return prev.count(0);
	} 
};