[考試總結]ZROI-21-CSP7連-DAY3 總結
#T1 斯諾克
#題意簡述
\(7\) 種物品,第 \(i\) 種物品的價值為 \(i\),有 \(a_i\) 個,必須按照價值自小到大地選,特別的,必須將第 \(1\) 種選完才能向後選,選第 \(1\) 種時可以得到一個額外價值 \(x\),可以在當前 \(a_i>0(i\ne1)\) 的物品價值中任選。
#大體思路
顯然只要有就一定可以都選上,附加權值直接貪心的選最大的即可。
#Code
int rcnt, a[8], res, mx; int main() { for (int i = 1; i <= 7; ++ i) { scanf("%d", &a[i]); res += a[i] * i; if (a[i]) mx = i; } res += a[1] * mx; printf("%d", res); return 0; }
#T2 翻轉
#題意簡述
給定一個 \(n\times m(n,m\leq17)\) 的 \(01\) 矩陣,每次操作可以將如下圖範圍的圖形做異或操作,圖形可以不全(在邊界),但中心點必須位於矩陣中。
問至少需要操作多少次才能將矩陣全部變為 \(0\)。
#大體思路
不難發現
- 對於一個位置最多被操作一次;
- 對於兩個操作位置相同的操作聚合,結果相同;
於是可以想到自上而下的確定情況,注意到如果將第一行的狀態確定,每次只對已經確定的行的下一行進行操作,那麼下面怎麼變換都是確定的了,於是不難得到以下演算法:用二進位制列舉第一行的每個點的操作狀態,對原圖進行修改,並在改後的圖上進行模擬/統計即可,時間複雜度為 \(O(nm\cdot2^m)\)
#Code
const int N = 200010; const int INF = 0x3fffffff; template <typename T> inline T Min(const T a, const T b) {return a < b ? a : b;} int n, m, ans = INF, mp[20][20], tmp[20][20]; inline int bitcount(int x) { int cnt = 0; while (x) cnt += (x & 1), x >>= 1; return cnt; } int get_res(int x) { int res = 0; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) tmp[i][j] = mp[i][j]; for (int i = 0; i < m; ++ i) if (x & (1 << i)) { tmp[1][i + 1] ^= 1, tmp[1][i] ^= 1; tmp[1][i + 2] ^= 1, tmp[2][i + 1] ^= 1; } for (int i = 1; i < n; ++ i) for (int j = 1; j <= m; ++ j) if (tmp[i][j]) { ++ res; tmp[i][j] ^= 1; tmp[i + 1][j] ^= 1; tmp[i + 1][j - 1] ^= 1; tmp[i + 1][j + 1] ^= 1; tmp[i + 2][j] ^= 1; } for (int i = 1; i<= m; ++ i) if (tmp[n][i]) return INF; return res + bitcount(x); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) { char c; cin >> c; if (c == 'X') mp[i][j] = 1; else mp[i][j] = 0; } for (int i = 0; i < 1 << m; ++ i) ans = Min(ans, get_res(i)); cout << ans; return 0; }
#T3 數對
#題意簡述
給定兩個數列 \(\{a_1,a_2,\dots,a_n\}\) 和 \(\{b_1,b_2,\dots,b_m\}(n,m\leq10^5,1\leq a_i,b_i\leq2^30)\),問有多少數對 \((a_i,b_j)(1\leq i\leq n,1\leq j\leq m)\),滿足 \(\text{bitcount}(a_i\text{ xor }b_j)=2\).
#大體思路
\(\text{bitcount}(a_i\text{ xor }b_j)=2\) 意味著 \(a_i,b_j\) 在二進位制下只有兩位不同,維護一個 Hash,每次列舉不同的位置中的一位 \(k\),先查詢 Hash 中每個 \(b_j\text{ xor }2^k\) 出現的次數,再將每個 \(a_i\text{ xor }2^k\) 加入 Hash,不難發現這樣統計每對數字剛好被統計一次,即得答案。時間複雜度 \(O(30\cdot n)\),\(30\) 為列舉 \(k\) 的複雜度。
#Code
const int N = 20000010;
const int MOD = 19260817;
const int INF = 0x3fffffff;
struct Hash {int val, tot, nxt;} h[N];
int n, m, a[N], b[N], head[N], cnt;
ll ans;
inline void insert(int x, int c) {
int hx = x % MOD, p = head[hx];
while (p && h[p].val != x) p = h[p].nxt;
if (h[p].val == x) h[p].tot += c;
else {
h[++ cnt].val = x, h[cnt].tot = c;
h[cnt].nxt = head[hx], head[hx] = cnt;
}
}
inline int Count(int x) {
int hx = x % MOD, p = head[hx];
while (p && h[p].val != x) p = h[p].nxt;
if (h[p].val == x) return h[p].tot;
else return 0;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++ i)
scanf("%d", &a[i]);
for (int i= 1; i <= m; ++ i)
scanf("%d", &b[i]);
for (int i = 1; i <= 30; ++ i) {
int k = 1 << (i - 1);
for (int j = 1; j <= m; ++ j)
ans += Count(b[j] ^ k);
for (int j = 1; j <= n; ++ j)
insert(a[j] ^ k, 1);
}
printf("%lld", ans);
return 0;
}
#T4 迴文串
#題意簡述
定義一個字串的價值是它的迴文子串的個數。現給定一字串 \(s(|s|\leq10^5)\),可以任一改變一個字元,問可得到的最大價值。
#大體思路
考慮改變一個位置會造成的影響:失去一些迴文子串、得到新的迴文子串。
在考慮改變造成的影響前,我們需要先得到在每個位置的迴文半徑,可以用 Manacher 求,當然也可以用 Hash+二分完成,這裡直接用的 Hash。然後,我們再來考慮造成的影響。
對於每個迴文中心 \(i,j(i\leq j)\) 和其對應的迴文半徑 \(r\),考慮得到新的迴文子串時對應的改變位置要麼是 \(x=i-r\) 要麼是 \(y=j+r\),且一定是改成對應的另一個的字元,否則對於這個迴文中心,不會造成價值增加。
對於奇迴文,\(i=j\),對於偶迴文,\(j=i+1\)。
之後同樣可以用 Hash+二分求得不包括 \(x\) 和 \(y\) 的新增半徑(即為新增的迴文子串的數量),用陣列 \(f_{i,c}\) 進行儲存,其中 \(f\) 的意義是 “將第 \(i\) 個位置變為 \(c\) 字元增加的迴文子串的數量”。
下面來考慮修改 \(x\) 失去的迴文串數量,顯然對於當前迴文中心 \(i,j\),若 \(i-r+1\leq x\leq i\),那麼減少的個數為 \(r-(i-x)=r-i+x\),若 \(j\leq x\leq j+r-1\),那麼減少的個數為 \(j+r-1-(x-1)=j+r-x\),發現這兩個貢獻對於修改位置 \(x\) 都可以拆分成兩部分:\(r-i\) 和 \(x\)、\(r+j\) 和 \(-x\),其中僅關於 \(i,j,r\) 的部分與修改哪裡無關,於是可以維護一個數組 \(fl_x\),記錄這一部分的貢獻,這裡的區間加法可以用差分處理;再來看 \(x\) 與 \(-x\),同樣可以維護差分陣列 \(fk_x\),對 \(fk\) 只做區間加 \(1\) 與加 \(-1\),最後統計貢獻可以統一乘上 \(x\)。
綜上我們對於修改的影響都已經解決了,最後列舉修改位置及修改後的字元,將貢獻取最大值與初始價值相加即得答案。時間複雜度為 \(O(n\log n+26\cdot n)\).
#Code
#define ll long long
#define ull unsigned long long
const int N = 200010;
const ull BASE = 9973;
const ull MOD = 1e9 + 7;
const int INF = 0x3fffffff;
template <typename T>
inline T Min(const T a, const T b) {return a < b ? a : b;}
template <typename T>
inline T Max(const T a, const T b) {return a > b ? a : b;}
int n, p[N], h[N], hr[N]; char s[N], sr[N];
ll f[N][26], fk[N], fl[N], init_val;
inline void init_hash(char *S, int *H) {
int len = strlen(S); H[0] = 1;
for (int i = 0; i < len; ++ i)
H[i + 1] = (1ull * H[i] * BASE % MOD + (ll)(S[i] - 'a')) % MOD;
}
inline ull getHash(int* H, int a, int b) {
return ((1ull * H[b + 1] - 1ull * H[a] * p[b - a + 1] % MOD) + MOD) % MOD;
}
inline bool check(int a, int b, int c, int d) {
return getHash(h, a, b) == getHash(hr, n - d - 1, n - c - 1);
}
int getRadius(int l, int r, int x, int y){
int L = x, R = y, res = 0;
while (L <= R) {
int mid = (L + R + 1) >> 1;
if (check(l - mid + 1, l, r, r + mid - 1))
res = mid, L = mid + 1;
else R = mid - 1;
}
return res;
}
signed main() {
scanf("%s", s); n = strlen(s); p[0] = 1;
for (int i = 0; i <= n; ++ i)
p[i + 1] = (1ull * p[i] * BASE) % MOD;
for (int i = 0; i < n; ++ i)
sr[i] = s[n - i - 1];
init_hash(s, h); init_hash(sr, hr);
for (int i = 0; i < n; ++ i)
for (int j = i; j <= i + 1; ++ j) {
int r = 0; r = getRadius(i, j, 0, Min(i + 1, n - j));
if (i == j) {
fk[j + 1] += -1, fk[j + r] -= -1;
fl[j + 1] += j + r, fl[j + r] -= j + r;
fk[i - r + 1] += 1, fk[i] -= 1;
fl[i - r + 1] += -(i - r), fl[i] -= -(i - r);
} else {
fk[j] += -1, fk[j + r] -= -1;
fl[j] += j + r, fl[j + r] -= j + r;
fk[i - r + 1] += 1, fk[i + 1] -= 1;
fl[i - r + 1] += -(i - r), fl[i + 1] -= -(i - r);
}
int x = i - r, y = j + r; init_val += r;
if (x < 0 || y >= n) continue;
++ r; r = getRadius(x - 1, y + 1, 0, Min(i + 1, n - j) - r);
f[x][s[y] - 'a'] += r + 1;
f[y][s[x] - 'a'] += r + 1;
}
ll best_change = 0, k = 0, l = 0;
for (int i = 0; i < n; ++ i) {
k += fk[i], l += fl[i];
for (int j = 0; j < 26; ++ j)
if (j + 'a' != s[i])
best_change = Max(best_change, f[i][j] - (1ll * i * k + l));
}
printf("%lld\n", init_val + best_change);
return 0;
}