2014多校聯合四(HDU 4901 HDU 4902 HDU 4905),hdu4902
HDU 4901 The Romantic Hero
題意: 一串數字a 找一個位置分開 前面為S'後面為T' 從這兩個集合中分別選出子集S和T 使得S中元素的“異或”值等於T中元素的“且”值 問一共幾種方案
思路:
由於a[i]只有1024 那麼無論怎麼運算都不可能大於2047 又因為S和T有一個明顯的分界 所以我們可以想到利用dp分左右兩邊處理 令l[i][j]表示從左到i位置且一定選取a[i]的情況下異或值為j的方案數 r[i][j]類似 令sl[i][j]表示l[1~i][j]的和 sr[i][j]類似 這些都可以通過正反掃描得到 最後為了防止重複計數 可以通過sl[i][j]*r[i+1][j]或者sr[i][j]*l[i-1][j]來更新答案
代碼:
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 1010#define mod 1000000007#define M 2048int l[N][M], r[N][M], sl[N][M], sr[N][M], a[N];int t, n, ans;int main() {int i, j, v;scanf("%d", &t);while (t--) {scanf("%d", &n);for (i = 1; i <= n; i++)scanf("%d", &a[i]);memset(l, 0, sizeof(l));memset(sl, 0, sizeof(sl));l[1][a[1]] = sl[1][a[1]] = 1;for (i = 2; i <= n; i++) {for (j = 0; j < M; j++) {v = j ^ a[i];l[i][v] = sl[i - 1][j];}l[i][a[i]] = (l[i][a[i]] + 1) % mod;for (j = 0; j < M; j++)sl[i][j] = (sl[i - 1][j] + l[i][j]) % mod;}memset(r, 0, sizeof(r));memset(sr, 0, sizeof(sr));r[n][a[n]] = sr[n][a[n]] = 1;for (i = n - 1; i >= 1; i--) {for (j = 0; j < M; j++) {v = j & a[i];r[i][v] = (r[i][v] + sr[i + 1][j]) % mod;}r[i][a[i]] = (r[i][a[i]] + 1) % mod;for (j = 0; j < M; j++)sr[i][j] = (sr[i + 1][j] + r[i][j]) % mod;}ans = 0;for (i = 1; i < n; i++) {for (j = 0; j < M; j++) {if (sl[i][j] && r[i + 1][j]) {ans = ((__int64 ) sl[i][j] * r[i + 1][j] % mod + ans) % mod;}}}printf("%d\n", ans);}return 0;}
HDU 4902 Nice boat
題意: n個數字 m個操作 每次1操作將[l,r]區間所有值改為x 每次2操作將[l,r]中大於x的數改為與x取gcd
思路:明顯是線段樹 不過想不出對於一段區間進來兩次2操作如何合并 想想要更新到葉子節點(這裡的葉子指的是一段連續區間的數字相同) 就覺得線段樹也最佳化不到哪去 於是開始玩暴力 結果500+ms就過了…
暴力方法很簡單 因為如果1操作覆蓋了f這個點 那麼這個1操作前面的所有操作都沒有意義 因此暴力枚舉n個位置 對於每個位置從後到前掃描操作 如果遇到1操作就break 然後把掃描進來的2操作暴力做一遍 可以類比棧來實現
代碼:
#include<cstdio>#include<algorithm>using namespace std;__int64 last[100010], a[100010], num[100010];int l[100010], r[100010], op[100010];int n, m, t;__int64 func(__int64 fa) {if (fa < 0)return -fa;return fa;}__int64 kgcd(__int64 fa, __int64 fb) {if (fa == 0)return fb;if (fb == 0)return fa;if (!(fa & 1) && !(fb & 1))return kgcd(fa >> 1, fb >> 1) << 1;else if (!(fb & 1))return kgcd(fa, fb >> 1);else if (!(fa & 1))return kgcd(fa >> 1, fb);elsereturn kgcd(func(fa - fb), min(fa, fb));}int main() {int i, j, top;__int64 ans;scanf("%d", &t);for (; t; t--) {scanf("%d", &n);for (i = 1; i <= n; i++)scanf("%I64d", &a[i]);scanf("%d", &m);for (i = 1; i <= m; i++)scanf("%d%d%d%I64d", &op[i], &l[i], &r[i], &num[i]);for (i = 1; i <= n; i++) {top = 0;ans = a[i];for (j = m; j >= 1; j--) {if (i >= l[j] && i <= r[j]) {if (op[j] == 1) {ans = num[j];break;} else {last[++top] = num[j];}}}for (j = top; j > 0; j--) {if (ans > last[j])ans = kgcd(ans, last[j]);}printf("%I64d ", ans);}putchar('\n');}return 0;}
HDU 4905 The Little Devil II
陳題不多說了 四邊形最佳化
注意:不要迷信什麼快速gcd 我隊友因為他TLE了好幾次 為什麼呢? 演算法是錯的? 不是的… 在期望情況下明顯快速gcd更優 不過這題的gcd是一個區間裡所有數的gcd 由於數字是隨機的 所以很容易就產生小數字 因此還是輾轉相除靠譜
代碼:
#include<cstdio>#include<cstring>#include<algorithm>typedef __int64 ll;using namespace std;const int M = 3010;int a[M], s[M][M], g[M][M];ll dp[M][M];int n;template<class T>inline void scan_d(T &ret) {char c;ret = 0;while ((c = getchar()) < '0' || c > '9');while (c >= '0' && c <= '9')ret = ret * 10 + (c - '0'), c = getchar();}inline int abs(int a) {return a < 0 ? -a : a;}int gcd(int a, int b) {if (a < b)swap(a, b);int i;while (b) {i = a % b;a = b;b = i;}return a;}inline void solve() {int l, i, j, k;memset(dp, 0, sizeof(dp));for (i = 1; i <= n; i++)s[i][i] = i;for (l = 1; l <= n - 1; l++) {for (i = 1; i <= n - l; i++) {j = i + l;for (k = s[i][j - 1]; k <= s[i + 1][j]; k++) {if (k < j && dp[i][j] < dp[i][k] + dp[k + 1][j] + g[i][j]) {dp[i][j] = dp[i][k] + dp[k + 1][j] + g[i][j];s[i][j] = k;}}}}}int main() {int min, i, j;int T;scan_d(T);while (T--) {scan_d(n);ll max = 0;ll s = 0;for (i = 1; i <= n; i++) {scan_d(a[i]);s += a[i];}for (i = 1; i <= n; i++) {g[i][i] = a[i];for (j = i + 1; j <= n; j++) {g[i][j] = gcd(g[i][j - 1], a[j]);}}solve();printf("%I64d\n", s + dp[1][n]);}return 0;}
PS:比賽的時候腦子還是不太靈TAT 總是看到別人出題才深入思考… sad… 同時團隊配合還要加強!! 加油!!