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;
}
陈题不多说了 四边形优化
注意:不要迷信什么快速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),布布扣,bubuko.com
2014多校联合四(HDU 4901 HDU 4902 HDU 4905)
原文地址:http://blog.csdn.net/houserabbit/article/details/38322337