SRM 427 Div1 Hard

問題概要

n個の整数a[i]が与えられる。
これを、任意の隣り合う二つの数a[k],a[k+1]について(a[k]-a[k+1])%p==0が成り立たないように並べ替える場合の数をmod 1234567891で求めよ。

制約条件

n≦30
-10^6≦a[i]≦10^6
p≦1000

方針

nが30と大きいので愚直にビットDPすることはできないので工夫する必要がある。
まず、全ての数はmod pで考えて良い。

mod pで数が
2 2 3 4のようになったときに、
その個数のvectorを{2,1,1}とする。

このvectorをソートしたものと、直前の数(=禁止するグループの数)を状態とするDPをすると、状態数が減って解ける。
この状態からは次のような状態ができる。
{1,1,1}で個数1のグループが禁止。
{2,1}で個数0のグループが禁止。

禁止の個数のグループが複数ある場合、
全てのグループは対照的なので、一つだけのグループを禁止すると考えて問題ない。

ソースコード
map<pair<vi,int>,int> dp;
int go(vi v,int prev){
  int n=v.size();

  sort(all(v),greater<int>());
  if(v[n-1]==0)v.pop_back(), n--;
  
  if(n==0)return 1;
  if(dp.count(mp(v,prev)))return dp[mp(v,prev)];

  ll ret=0;
  rep(i,n){
    if(v[i]==prev&&(i==0||v[i]!=v[i-1]))continue;
    v[i]--;
    ret+=(ll)(v[i]+1)*go(v,v[i])%mod;
    v[i]++;
  }
  return dp[mp(v,prev)]=int(ret%mod);
}
class PSequence {
  public:
  int count(vector <int> S, int p) {
    dp.clear();

    int s[1000]={};
    rep(i,S.size())s[(S[i]%p+p)%p]++;
    sort(s,s+1000);
    vi v;
    rep(i,1000)if(s[i])v.pb(s[i]);
    return go(v,0);
  }
};