1. 程式人生 > 實用技巧 >「LibreOJ β Round #2」貪心只能過樣例

「LibreOJ β Round #2」貪心只能過樣例

知識點: bitset,01 揹包

原題面 Loj


題意簡述

給定 \(n\) 個數,\(x_i\) 的取值範圍 \([a_i,b_i]\)
求不同的 \(\sum\limits_{i=1}^{n}x_{i}^{2}\) 的種類數。
\(1\le n,a_i,b_i\le 100\)


分析題意

資料範圍比較喜人,\(\sum x_i^2\) 的值域較小,考慮一波暴力。
開 n 個 vector,記錄 \(1\sim i\) 能組成的所有數。
對於第 \(i+1\) 個數,列舉 \(1\sim i\) 能組成的所有數進行轉移,可以寫出下面的暴力程式碼。
\(n,a_i,b_i\) 同階,複雜度是 \(O(n^4)\)

級別。


發現這個轉移很像 01 揹包的轉移,加之值域較小。
考慮直接用 01 揹包求能組成哪些數。

\(f_{i,j} = (0/1)\) 表示當前列舉到第 \(i\) 個數,能否通過 \(1\sim i\) ,組成 \(j\)
有狀態轉移方程:

\[f_{i,j} = [\exist x\in [a_i,b_i],[f_{i-1,j-x^2}]] \]

01 揹包倒序列舉消去第一維,複雜度 \(O(n^2)\) 級別。


發現用個桶判定出現這裡好傻逼啊,考慮用 bitset 進行維護。
轉移與上述 01 揹包的方法本質相同,直接右移或起來即可。


程式碼實現

正解

//知識點:bitset 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <bitset>
#define ll long long
const int kMaxn = 110;
//=============================================================
int n;
std :: bitset <kMaxn * kMaxn * kMaxn> vis[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
//=============================================================
int main() {
  n = read();
  vis[0][0] = true;
  for (int i = 1; i <= n; ++ i) {
    int a = read(), b = read();
    for (int j = a; j <= b; ++ j) {
      vis[i] |= (vis[i - 1] << (j * j));
    }
  }
  printf("%d\n", vis[n].count());
  return 0;
}

暴力

//傻逼暴力/cy
//判定出現這裡好傻逼啊,改成bitset試試 
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <vector>
#define ll long long
const int kMaxn = 110;
//=============================================================
int n;
bool vis[kMaxn][kMaxn * kMaxn * kMaxn];
std :: vector <int> ans;
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
//=============================================================
int main() {
  n = read();
  ans.push_back(0);
  for (int i = 1; i <= n; ++ i) {
    int a = read(), b = read();
    std :: vector <int> tmp;
    for (int j = a; j <= b; ++ j) {
      int x = j * j;
      for (int k = 0, size = ans.size(); k < size; ++ k) {
        int now = ans[k] + x;
        if (! vis[i][ans[k] + x]) {
          vis[i][ans[k] + x] = true;
          tmp.push_back(ans[k] + x);
        }
      }
    }
    ans.clear();
    for (int k = 0, size = tmp.size(); k < size; ++ k) {
      ans.push_back(tmp[k]);
    }
  }
  printf("%d\n", ans.size());
  return 0;
}