小沙的签到题

小沙的签到题

题目link

题目描述

给定一段长度为 \(n(n \le 2 \times 10 ^ 5)\) 的序列 \(a(a_i \le 999999)\),求序列中有多少对数相加不会产生 10 进制的进位。

第一种做法是六维前缀和,考虑 s[bit1][bit2][bit3][bit4][bit5][bit6] 表示六位分别低于对应 bit 的方案数

预处理出前缀和数组之后, \(O(1)\) 查询结果,最终答案除以 \(2\) 即可。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define rep(i, a, b) for(int i(a); i < b; ++ i)

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 10;

int sum[N][N][N][N][N][N];

int get(char c) {
  return c - '0';
}

void add(string s) {
  sum[get(s[0])][get(s[1])][get(s[2])][get(s[3])][get(s[4])][get(s[5])] ++;
}

int Sum(string s) {
  return sum[get(s[0])][get(s[1])][get(s[2])][get(s[3])][get(s[4])][get(s[5])];
}


void solve() {
  int n; cin >> n;
  vector<string> a(n), b(n);
  for(int i = 0; i < n; i ++ ) {
    string s; cin >> s;
    reverse(s.begin(), s.end());
    while(s.size() < 6) s.push_back('0');
    reverse(s.begin(), s.end());
    a[i] = s;
    add(s);
    for(int j = 0; j < 6; j ++ ) {
      char &c = s[j];
      c = ('9' - c) + '0';
    }
    
    b[i] = s;
  }

  rep(i1, 1, 10) {
    rep(i2, 0, 10) {
      rep(i3, 0, 10) {
        rep(i4, 0, 10) {
          rep(i5, 0, 10) {
            rep(i6, 0, 10) {
              sum[i1][i2][i3][i4][i5][i6] += sum[i1 - 1][i2][i3][i4][i5][i6];
            }
          }
        }
      }
    }
  }

  rep(i1, 0, 10) {
    rep(i2, 1, 10) {
      rep(i3, 0, 10) {
        rep(i4, 0, 10) {
          rep(i5, 0, 10) {
            rep(i6, 0, 10) {
              sum[i1][i2][i3][i4][i5][i6] += sum[i1][i2 - 1][i3][i4][i5][i6];
            }
          }
        }
      }
    }
  }
  rep(i1, 0, 10) {
    rep(i2, 0, 10) {
      rep(i3, 1, 10) {
        rep(i4, 0, 10) {
          rep(i5, 0, 10) {
            rep(i6, 0, 10) {
              sum[i1][i2][i3][i4][i5][i6] += sum[i1][i2][i3 - 1][i4][i5][i6];
            }
          }
        }
      }
    }
  }
  rep(i1, 0, 10) {
    rep(i2, 1, 10) {
      rep(i3, 0, 10) {
        rep(i4, 1, 10) {
          rep(i5, 0, 10) {
            rep(i6, 0, 10) {
              sum[i1][i2][i3][i4][i5][i6] += sum[i1][i2][i3][i4 - 1][i5][i6];
            }
          }
        }
      }
    }
  }
  rep(i1, 0, 10) {
    rep(i2, 1, 10) {
      rep(i3, 0, 10) {
        rep(i4, 0, 10) {
          rep(i5, 1, 10) {
            rep(i6, 0, 10) {
              sum[i1][i2][i3][i4][i5][i6] += sum[i1][i2][i3][i4][i5 - 1][i6];
            }
          }
        }
      }
    }
  }
  rep(i1, 0, 10) {
    rep(i2, 1, 10) {
      rep(i3, 0, 10) {
        rep(i4, 0, 10) {
          rep(i5, 0, 10) {
            rep(i6, 1, 10) {
              sum[i1][i2][i3][i4][i5][i6] += sum[i1][i2][i3][i4][i5][i6 - 1];
            }
          }
        }
      }
    }
  }
  // debug(sum[10][10][10][10][10][10]);
  vector<int> ans1(n);
  ll sum = 0;
  for(int i = 0; i < n; i ++ ) {
    bool flag = true;
    for(int j = 0; j < 6; j ++ ) {
      if((a[i][j] - '0') + (a[i][j] - '0') > 9) {
        flag = false;
      }
    }
    if(flag) sum --;
    sum += Sum(b[i]);
  }

  cout << sum / 2;

}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
  return 0;
}

第二种解法:和第一种解法思路类似,维护的时候使用六维树状数组即可,注意需要用 \(cnt\) 数组进行去重,即每个数字只计算一次,不然会喜提 TLE

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define rep(i, a, b) for(int i(a); i <= b; ++ i)

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 11;

ll tr[N][N][N][N][N][N];

void add(int a[], ll v) {
  for(int i1 = a[0]; i1 <= 10; i1 += (i1 & -i1)) {
    for(int i2 = a[1]; i2 <= 10; i2 += (i2 & -i2)) {
      for(int i3 = a[2]; i3 <= 10; i3 += (i3 & -i3)) {
        for(int i4 = a[3]; i4 <= 10; i4 += (i4 & -i4)) {
          for(int i5 = a[4]; i5 <= 10; i5 += (i5 & -i5)) {
            for(int i6 = a[5]; i6 <= 10; i6 += (i6 & -i6)) {
              tr[i1][i2][i3][i4][i5][i6] += v;
            }
          }
        }
      }
    }
  }
}

ll sum(int a[], ll ret = 0) {
  for(int i1 = a[0]; i1 > 0; i1 -= (i1 & -i1)) {
    for(int i2 = a[1]; i2 > 0; i2 -= (i2 & -i2)) {
      for(int i3 = a[2]; i3 > 0; i3 -= (i3 & -i3)) {
        for(int i4 = a[3]; i4 > 0; i4 -= (i4 & -i4)) {
          for(int i5 = a[4]; i5 > 0; i5 -= (i5 & -i5)) {
            for(int i6 = a[5]; i6 > 0; i6 -= (i6 & -i6)) {
              ret += tr[i1][i2][i3][i4][i5][i6];
            }
          }
        }
      }
    }
  } 
  return ret;
}

constexpr int M = 1000010;

void solve() {
  int n; cin >> n;
  vector<int> cnt(M);

  for(int i = 0; i < n; i ++ ) {
    int x; cin >> x; cnt[x] ++;
  } 
  for(int i = 0; i < M; i ++ ) if(cnt[i]) {
    int b[6]; int x = i;
    for(int j = 5; j >= 0; j -- ) {
      b[j] = x % 10 + 1;
      x /= 10;
    }
    add(b, cnt[i]);
  }

  int c[6] = {10, 10, 10, 10, 10, 10};
  
  ll ans = 0;
  for(int i = 0; i < M; i ++ ) if(cnt[i]) {
    int b[6]; int x = i;
    for(int j = 5; j >= 0; j -- ) {
      b[j] = x % 10 + 1;
      x /= 10;
    }
    bool flag = true;
    for(int j = 5; j >= 0; j -- ) {
      if(b[j] + b[j] - 2 > 9) {
        flag = false;
      }
    }
    if(flag) ans += 1ll * cnt[i] * (cnt[i] - 1) / 2;
    add(b, -cnt[i]);
    for(int j = 5; j >= 0; j -- ) {
      b[j] = 11 - b[j];
    }
    ans += sum(b) * cnt[i];
    
  }

  cout << ans << "\n";


}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
#ifdef LOCAL
  clock_t ends = clock();
  cout << "\n\nRunning Time : " << (double) (ends - start) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
#endif
  return 0;
}

第三种做法:考虑 dp[i][j] 表示前三位是 \(i\) 并且后三位不会和 \(j\) 产生进位的方案数,然后可以得到一个 \(O(10^6)\)\(DP\),具体过程可以看代码。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 1010;

int dp[N][N];
// dp[i][j] 表示高三位为 i,且低三位不会和 j 产生进位的方案数
void solve() {
  int n; cin >> n;
  ll ans = 0;
  for(int i = 0; i < n; i ++ ) {
    int num; cin >> num;
    int x = num / 1000, y = num % 1000;
    x = 999 - x, y = 999 - y;
    array<int, 3> a, b;
    a[0] = x / 100, a[1] = x / 10 % 10, a[2] = x % 10;
    b[0] = y / 100, b[1] = y / 10 % 10, b[2] = y % 10;
    x = 999 - x, y = 999 - y;
    for(int j = 0; j <= a[0]; j ++ ) {
      for(int k = 0; k <= a[1]; k ++ ) {
        for(int l = 0; l <= a[2]; l ++ ) {
          ans += dp[j * 100 + k * 10 + l][y];
        }
      }
    }

    for(int j = 0; j <= b[0]; j ++ ) {
      for(int k = 0; k <= b[1]; k ++ ) {
        for(int l = 0; l <= b[2]; l ++ ) {
          dp[x][j * 100 + k * 10 + l] ++;
        }
      }
    }

  }

  cout << ans << '\n';
}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
#ifdef LOCAL
  clock_t ends = clock();
  cout << "\n\nRunning Time : " << (double) (ends - start) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
#endif
  return 0;
}
posted @ 2022-09-08 16:46  ccz9729  阅读(26)  评论(0编辑  收藏  举报