少有的獨自做出來的 *3000,還是很有成就感的!
集中注意力讀題,首先注意到每一時刻牌數為 \(k\),而牌的種類也為 \(k\),如果實際牌的種類數小於 \(k\),那麼是很簡單的情況,現在考慮實際牌的種類數等於 \(k\) 的情況。
觀察過程,首先發現如果有相同的牌直接丟就行,過程中還會出現沒有牌相同的情況,顯然這種情況每個牌的數量為 \(1\),啟發我們使用 dp,設 \(f_i\) 表示已經考慮完 \(i\) 張牌且當前沒有相同牌,能獲得的最多硬幣數。
首先呼之欲出的是 \(\mathcal{O}(n^2k^2)\) 的愚蠢做法。就是列舉我們刪哪兩張牌,再模擬出後面第一次出現沒有相同牌狀態的位置,並完成轉移。
這樣做是低效的。我們發現我們可以直接轉移,在轉移中變成判定問題,\(f_j\) 能轉移到 \(f_i\),當且僅當區間 \([i+1,j]\) 從當前狀態繼續操作完成後出現無相同牌狀態,且中途一直有相同牌。注意到刪相同牌不會改變奇偶性,而加入會,我們發現這個判定過程實際上是一個異或加入牌的過程,而一個區間能滿足條件當且僅當存在兩種牌出現個數為奇數,直接異或雜湊即可。
還有一種很煩人的情況,就是永遠都不會出現無相同牌的狀態了,這時的答案與實際牌的種類數小於 \(k\) 的類似,他的答案就是 \(\sum_{i=1}^k \lfloor \frac{s_i}{2}\rfloor\)。\(s_i\) 表示第 \(i\) 種手牌的數量。然而,對於當前 \(f_i\) 的答案來說,\(s_i\) 是對於 \([i,n]\) 這個字尾算出來的,並且每個都會加一,因為當前每種牌各有一張。而這時我們要刪除兩張牌,顯然我們因儘可能刪除 \(s_i\) 為奇數的牌,因為這樣不會減少答案。列舉牌對顯然不優,但我們可以將判斷轉為簡單計數。
然後還有個細節就是起點問題,直接模擬找起點就行了。
總時間複雜度 \(\mathcal{O}(n^2)\)。
程式碼:
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define id(x, y) m * ((x) - 1) + (y)
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 1e3 + 5, P = 1e9;
constexpr double PI = acos (-1.0);
typedef unsigned long long ull;
inline int rd () {
int x = 0, f = 1;
char ch = getchar ();
while (! isdigit (ch)) {
if (ch == '-') f = -1;
ch = getchar ();
}
while (isdigit (ch)) {
x = (x << 1) + (x << 3) + ch - 48;
ch = getchar ();
}
return x * f;
}
int qpow (int x, int y) {
int ret = 1;
for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
return ret;
}
int n, k;
int a[N], b[N], f[N];
bitset <N> bit;
ull w[N];
unordered_map <ull, pii> mp;
unordered_map <ull, bool> mmp;
int s1[N][N], s[N];
int main () {
// freopen ("1.in", "r", stdin);
// freopen ("1.out", "w", stdout);
n = rd (), k = rd ();
rep (i, 1, n) a[i] = b[i] = rd ();
sort (b + 1, b + n + 1);
int kk = unique (b + 1, b + n + 1) - b - 1;
if (kk < k) {
rep (i, 1, n) ++ s[a[i]];
int sum = 0;
rep (i, 1, k) sum += s[i] / 2;
cout << sum;
return 0;
}
rep (i, 1, n) a[i] = lower_bound (b + 1, b + k + 1, a[i]) - b;
mt19937_64 rnd (time (0));
rep (i, 1, k) {
w[i] = rnd ();
rep (j, 1, i - 1) mp[w[i] ^ w[j]] = pii (i, j);
}
rrp (i, 1, n) {
ull now = 0;
rep (j, i, n) ++ s1[i][a[j]];
int s2[3] = {0, 0, 0}, s3[2] = {0, 0}, sum = 0;
rep (j, i, n) {
now ^= w[a[j]];
if (mp.find (now) != mp.end () && ! mmp[now]) {
pii p = mp[now];
mmp[now] = 1;
f[i - 1] = max (f[i - 1], f[j] + (j - i + 1) / 2 - 1);
int x = s1[i][p.first], y = s1[i][p.second];
if ((x & 1) ^ (y & 1)) ++ s2[1];
else if (x & 1) ++ s2[0]; else ++ s2[2];
}
}
now = 0;
rep (j, i, n) {
now ^= w[a[j]];
mmp[now] = 0;
}
rep (j, 1, k) sum += (s1[i][j] + 1) / 2, ++ s3[s1[i][j] & 1];
if (s3[1] * (s3[1] - 1) / 2 > s2[0]) f[i - 1] = max (f[i - 1], sum - 2);
if (s3[0] * s3[1] > s2[1]) f[i - 1] = max (f[i - 1], sum - 1);
if (s3[0] * (s3[0] - 1) / 2 > s2[2]) f[i - 1] = max (f[i - 1], sum);
}
int ans = -1;
rep (i, 1, n) {
if (bit[a[i]] == 1) bit[a[i]] = 0;
else bit[a[i]] = 1;
if (bit.count () == k) {
ans = max (ans, (i - k) / 2 + f[i]);
break;
}
}
if (ans == -1) {
rep (i, 1, n) ++ s[a[i]];
int sum = 0;
rep (i, 1, k) sum += s[i] / 2;
cout << sum; return 0;
}
cout << ans;
}