SRM 524 Div1 500 LongestSequence

問題

数列C[i]が与えられる。
C[i]の各項について、

  • C[i]が正なら、連続する任意のC[i]項が正
  • C[i]が負なら、連続する任意の-C[i]項が負

という制約をつける。
この制約を全て満たす数列のうち、長さが最大のものを答えよ。

無限に長い数列が作れる場合、-1を、
数列が作れない場合は0を返せ。

制約条件

  • 1000≦C[i]≦1000

Cの項数≦50

方針

S[i]=a[1]+a[2]+…+a[i]とする。
すると、a[i-k+1]からa[i]までの連続するk項の和が正⇔S[i-k]<S[i]が成り立つことがわかる。
負の場合は逆にS[i-k]>S[i]である。


ここで、Sの大小関係をグラフにする。
すなわち、小さいほうから大きい方に辺を貼ったグラフを考える。
これが閉路を持たないことが、条件を満たすS[i]が存在することに他ならない。

ソースコード

vector<vi> g;
int v[500];
bool dfs(int c){
  v[c]=-1;
  rep(i,g[c].size()){
    if(v[g[c][i]]==-1)return 0;
    if(!v[g[c][i]]&&!dfs(g[c][i]))return 0;
  }
  v[c]=1;
  return 1;
}
bool ok(int n,vi &c){
  int m=c.size();
  g=vector<vi>(n+1);
  memset(v,0,sizeof(v));

  rep(i,m)for(int j=0;j+abs(c[i])<=n;j++){
    if(c[i]>0)g[j].pb(j+c[i]);
    else g[j-c[i]].pb(j);
  }
  rep(i,n+1)if(!v[i]&&!dfs(i))return 0;
  return 1;
}
class LongestSequence {
  public:
  int maxLength(vector<int> C)
  {
    int lo=-1,hi=5000,mid;
    while(lo+1<hi){
      mid=(lo+hi)/2;
      if(ok(mid,C))lo=mid;
      else hi=mid;
    }
    if(lo==-1)return 0;
    if(hi==5000)return -1;
    return lo;
  }
};