TopCoder SRM514 Div1Hard

問題

魔法少女回。魔法少女が呪文を唱える。呪文は0,1からなる文字列である。最初のK個の呪文(A[0]..A[K-1])は決まっている。i>=Kのとき、A[i] = A[i-1] + A[i-K-1] + A[i-2K-1] + …(i-nK-1>=0の限り繰り返し)である。呪文Sを、A[N]の[lo, hi]の部分とする。Sの中で、最も多く1が連続している部分の連続回数が魔法の強さである。これを求めよ。

解法

Nが途方もなく大きくて絶望しそうだが、A[i]はどんどん長い呪文になるので、実際は700程度でhiの上限を超えてしまうことがわかる。Nが大きすぎる場合は勝手に小さくしてよい。
後は、呪文の特徴のみをおさえた構造体を作り、再帰的に解けば良い。但し、そのまま解くと再帰回数が増えすぎて絶対間に合わないので、「A[i]全体について解くとき」だけ結果を覚えておいてメモ化する。それ以外の場合はほとんど発生しない(高々2N回程度)なので逐一計算する。

コード

#include <vector>
#include <string>
#include <algorithm>

using namespace std;

#define SIZE 700
typedef long long i64;

struct magic
{
	i64 left, right, ln, size;
	
	magic(){ left = right = ln = size = 0; }
};

magic operator+(const magic& L, const magic& R)
{
	magic ret;
	ret.size = L.size + R.size;
	ret.left = L.left + (L.left==L.size ? R.left : 0);
	ret.right = R.right + (R.right==R.size ? L.right : 0);
	ret.ln = max(L.right + R.left, max(max(ret.left, ret.right), max(L.ln, R.ln)));
	
	return ret;
}

class MagicalGirlLevelThreeDivOne
{
	vector<string> in;
	i64 len[SIZE];
	magic memo[SIZE];
	int K;
	
	magic calc(int N, int Lo, int Hi)
	{
		magic ret;
		ret.left = 0;
		for(int i=Lo;i<Hi;i++){
			if(in[N][i]=='0') break;
			ret.left++;
		}
		ret.right = 0;
		for(int i=Hi-1;i>=Lo;i--){
			if(in[N][i]=='0') break;
			ret.right++;
		}
		ret.size = Hi-Lo;
		ret.ln = 0;
		i64 p = 0;
		for(int i=Lo;i<Hi;i++){
			if(in[N][i]=='1'){
				 p++; ret.ln = max(ret.ln, p);
			}else p=0;
		}
		ret.ln = max(ret.ln, p);
		
		return ret;
	}
	
	magic solve(int N, i64 Lo, i64 Hi)
	{
		Lo = max(Lo, 0LL);
		Hi = min(Hi, len[N]);
		if(Lo >= Hi) return magic();
		
		if(Lo==0 && Hi==len[N]){
			if(memo[N].size==-1){
				if(N < K){
					return memo[N] = calc(N, (int)Lo, (int)Hi);
				}
				
				memo[N] = magic();
				for(int j=N-1;j>=0;j-=K){
					memo[N] = memo[N] + solve(j, 0, len[j]);
				}
			}
			return memo[N];
		}
		
		if(N < K){
			return calc(N, (int)Lo, (int)Hi);
		}
		
		magic ret;
		i64 pos = 0;
		for(int j=N-1;j>=0;j-=K){
			ret = ret + solve(j, Lo - pos, Hi - pos);
			pos += len[j];
		}
		return ret;
	}
	
public:
	i64 theMaxPower(vector<string> first, int N, i64 Lo, i64 Hi)
	{
		in = first;
		K = in.size();
		Hi++; //[Lo, Hi)
		
		for(int i=0;i<K;i++) len[i] = in[i].size();
		for(int i=K;i<=N;i++){
			len[i] = 0;
			for(int j=i-1;j>=0;j-=K) len[i] += len[j];
			if(len[i] > Hi){
				N = i;
				break;
			}
		}
		
		for(int i=0;i<=N;i++) memo[i].size = -1;
		
		magic ret = solve(N, Lo, Hi);
		return ret.ln;
	}
};