2014多校联合四(HDU 4901 HDU 4902 HDU 4905)

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…  同时团队配合还要加强!! 加油!!

你可能感兴趣的:(dp,HDU,杂题)