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 2048 int 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); else return 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… 同时团队配合还要加强!! 加油!!
2014多校联合四(HDU 4901 HDU 4902 HDU 4905)