1. 程式人生 > >SDOI2010 代碼拍賣會

SDOI2010 代碼拍賣會

方程 line 題意 using 明顯 problem pre lin 傳送門

SDOI2010 代碼拍賣會

題意:

題目傳送門

題解:

看完題目之後,第一反應應該就是數位\(Dp\)了,但是考慮到\(N\)非常的大,我們需要考慮另一種方法。註意到這個滿足條件的數字的每一位都大於等於前一位,所以我們可以比較明顯的發現,最後組成的數字一定可以表示成小於等於\(9\)\(111...111\)(若幹個\(1\))這樣形式的數字之和,隨後可以發現這些數字在模\(p\)的意義下是有循環節的,所以我們可以設計一個\(Dp\),記\(cnt[i]\)表示模\(p\)\(i\)\(111...111\)這樣的數字的個數,\(f[i][j][k]\)表示考慮了模\(p\)小於等於\(i\)

的數,當前數字模\(p\)\(j\),已經選了\(k\)\(111...111\)這樣的數字的方案數,轉移方程就是\(f[i][j][k] * \binom{cnt[i] + t - 1}{t} \to f[i + 1][(j + t \times i) \% p][k + t]\)。最後註意由於每一個數都是\(1\),所以\(1111...111\)\(N\)\(1\))是必選的。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 505;
const int Md = 999911659;
typedef long long ll;

inline int Add(const int &x, const int &y) { return (x + y >= Md) ? (x + y - Md) : (x + y); }
inline int Sub(const int &x, const int &y) { return (x - y < 0) ? (x - y  + Md) : (x - y); }
inline int Mul(const int &x, const int &y) { return (ll)x * y % Md; }
int Powe(int x, int y) {
  int ans = 1;
  for(;y; y >>= 1, x = Mul(x, x)) if(y & 1) ans = Mul(ans, x);
  return ans;
}

int f[N][N][10], cho[N][10], inv[10];
ll n;
ll cnt[N], st[N], num[N];
int p, sum, St, Ed;

int main() {
  scanf("%lld%d", &n, &p);
  for(int i = 1; i <= n; i++) {
    sum = sum * 10 + 1; sum %= p;
    if(st[sum]) {
      St = st[sum]; Ed = i;
      break;
    }
    cnt[sum]++; st[sum] = i; num[i] = sum;
  }
  int len = 0;
  if(St) {
    len = Ed - St;
    ll c = n - Ed + 1;
    ll d = c / len, m = c % len;
    for(int i = St; i < Ed; i++) cnt[num[i]] += d;
    for(int i = St; i < Ed && m; i++, m--) cnt[num[i]] ++;
  }
  cnt[0]++;
  inv[1] = 1;
  for(int i = 2; i <= 9; i++) inv[i] = Mul(Md - Md / i, inv[Md % i]);
  for(int i = 0; i < p; i++) {
    cnt[i] %= Md;
    cho[i][0] = 1;
    for(int j = 1; j <= 8; j++) {
      cho[i][j] = Mul(Mul(cnt[i] % Md, cho[i][j - 1]), inv[j]);
      cnt[i] = Add(cnt[i], 1);  
    }
  }
  if(!len) {
    f[0][num[n]][0] = 1;
    for(int i = 0; i < p; i++) {
      for(int j = 0; j < p; j++) {
        for(int k = 0; k <= 8; k++) {
          for(int t = 0; t <= k; t++) {
            if(!f[i][(j - t * i % p + p) % p][k - t]) continue; 
            f[i + 1][j][k] = Add(f[i + 1][j][k], Mul(f[i][(j - t * i % p + p) % p][k - t], cho[i][t]));
          }
        }
      }
    }
    printf("%d\n", f[p][0][8]);
    return 0;
  }
  int las = (n - Ed + 1) % len;
  if(!las) las += len;
  f[0][num[St + las - 1]][0] = 1;
  for(int i = 0; i < p; i++) {
    for(int j = 0; j < p; j++) {
      for(int k = 0; k <= 8; k++) {
        for(int t = 0; t <= k; t++) {
          if(!f[i][(j - t * i % p + p) % p][k - t]) continue; 
          f[i + 1][j][k] = Add(f[i + 1][j][k], Mul(f[i][(j - t * i % p + p) % p][k - t], cho[i][t]));
        }
      }
    }
  }
  printf("%d\n", f[p][0][8]);
  return 0;
}

SDOI2010 代碼拍賣會