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

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

时间: 2024-12-25 14:52:37

2014多校联合四(HDU 4901 HDU 4902 HDU 4905)的相关文章

2014多校第四场1005 || HDU 4901 The Romantic Hero (DP)

题目链接 题意 :给你一个数列,让你从中挑选一些数组成集合S,挑另外一些数组成集合T,要求是S中的每一个数在原序列中的下标要小于T中每一个数在原序列中下标.S中所有数按位异或后的值要与T中所有的数按位与的值相同,问能找出多少符合要求的组合. 思路 :比赛的时候有点没有头绪,后来二师兄想出了状态转移方程,YN又改了很多细节,最后才A的.总之是个别扭的DP..... 一开始是 _xor[i][j^a[i]] += _xor[i-1][j] :j 的下一个状态 就是异或上a[i],这个数组所代表的意思

2014多校联合七(HDU 4937 HDU 4938 HDU 4939 HDU 4941)

好几天没写题解了- 都怪我太弱  补题补不动- HDU 4937 Lucky Number 题意:一个数字如果只有3456这四种数字组成  那么这个数字是幸运的  问  给出一个x  它在几种进制下是幸运的  如果无穷输出-1 思路: 分类讨论  如果x是3或4或5或6  那么一定有无穷个进制满足(从十进制开始-)  直接输出-1 除去上述情况  那么我们可以将一个数字写成这样 a0 + a1*base + a2*base^2 +... = x 那么如果base的最高次只有1次或2次时  就是简

hdu 4865 Peter&amp;#39;s Hobby(2014 多校联合第一场 E)

Peter's Hobby Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 545    Accepted Submission(s): 237 Problem Description Recently, Peter likes to measure the humidity of leaves. He recorded a leaf

hdu 4865 Peter&#39;s Hobby(2014 多校联合第一场 E)

Peter's Hobby Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 545    Accepted Submission(s): 237 Problem Description Recently, Peter likes to measure the humidity of leaves. He recorded a leaf

2014多校联合六(HDU 4923 HDU 4925 HDU 4927 HDU 4930)

HDU 4923 Room and Moor 题意:给出A序列  求满足题目所写的B序列  使得方差最小 思路:可以想到最后的结果中  B序列的值一定是一段一段的  那么我们可以类似贪心去搞  对于一段序列我们可以求出什么样的b值使得方差最小  即序列中1的个数除以序列长度  又因为B是单调的  可以用一个单调栈去模拟  复杂度远远小于n^2  不要被吓怕- 代码: #include<cstdio> #include<cstring> #include<algorithm&g

2014多校联合三 (HDU 4888 HDU 4891 HDU 4893)

HDU 4891 The Great Pan 签到题  他怎么说你就怎么做就好了  注意做乘法时候会爆int 代码: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; int n; char s[2000000]; int flag1, flag2, sp, ansflag;

2014多校联合五(HDU 4911 HDU 4915 HDU 4920)

HDU 4911 Inversion 题意:n个数字  通过k次相邻交换  使得逆序对数最少 思路:如果序列为 XXXABYYY  假设A和B位置互换  易知X和AB.Y和AB的逆序对数不变  换句话说一次交换最多使逆序对减少1  那么只需要求原逆序对数和k进行比较即可 代码: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100100 type

2014多校联合八(HDU 4945 HDU 4946 HDU 4948 HDU 4950 HDU 4951 HDU 4952)

HDU 4945 2048 题意:给你一堆数字  问有几个子集可以拼出2048 思路: 拼数字的规则相当于让数字乘二  所以不是2^i的数字不会拼出2048  那么这些数可选可不选  即为2^cnt种可能 之后只要计算出有几个子集不可能拼出2048即可  不过简单的直接dp是2048*100000的复杂度的  会TLE 所以要变成先枚举元素  再枚举该种元素个数  再枚举2048种状态  然后利用组合求(组合里需要逆元) 为什么这样快?  因为比如枚举2^5这个元素的时候  最多取2^6个  枚

hdu 4865 Peter&#39;s Hobby(2014 多校联合第一场 E)

题意:已知昨天天气与今天天气状况的概率关系(wePro),和今天天气状态和叶子湿度的概率关系(lePro)第一天为sunny 概率为 0.63,cloudy 概率 0.17,rainny 概率 0.2.给定n天的叶子湿度状态,求这n天最可能的天气情况 分析:概率dp设 dp[i][j] 表示第i天天气为j的最大概率,pre[i][j]表示第i天天气最可能为j的前一天天气,dp[i][j]=max(dp[i-1][k]+log(wePro[k][j])+log(lePro[j][lePos[i]]