Codeforces Round #198(Div.2)ABCDE

这本来应该是一场AK的比赛,结果自己没把握住机会,有点遗憾。其实除了D是看了题解之后顿悟的,其他题目都不难,比赛时候写不出来,也只能说自己水平不够,因此写篇博客纪念。

题目链接:http://codeforces.ru/contest/340

A:

一直是水题的一道题,每次要抓紧时间,我现在最快也就4分钟A这题。。。英语实在捉急。。。题目就是求一个最小公倍数。然后区间减法。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const int N = 100005;
using namespace std;

LL gcd(LL a, LL b) {return a % b == 0 ? b : gcd(b, a % b);}

int main()
{
		LL re, n, m, i, j, k, x, y, a, b, c;

		cin >> x >> y >> a >> b;
		c = x * y / gcd(x, y);
		cout << (b) / c - (a - 1) / c << endl;

		return 0;
}

B:

B是个几何问题,没A掉纯属自己太胆小,题目就是给一个300个点的集合,没有三点共线,就其中有最大面积的那个四边形的面积。(这里的四边形可以是凹的),第一眼看到就想把四边形沿对角线分成两个三角形,然后在对角线两边分别求最大三角形面积,但是当时自己粗略估计了一下时间复杂度,O(n^3),我还以为n范围是10^5就没敢做,后来虽然看到n是300,但一想,27000000可能也会TLE,这应该不是题解要的算法,结果比完才发现,题解的标准算法是O(n^3)的暴力搜索。只怪自己没胆量,顺便说一句,一般1秒能接受的运算次数是10^8,O(n^3)的算法一秒的话,n可以大至700左右(同学交流的经验)。既然是暴力代码实现就很简单了,三角形面积用叉乘来算,正负号则刚好说明了点在对角线的不同侧,注意这题一定要在两边都有点,否则取到的最大值并不是我们想要的结果。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const int N = 305;
using namespace std;

LL a[N], x[N], y[N];

int main()
{
		LL re, n, m, i, j, k,u, d, s;
		
		/*ios :: sync_with_stdio(false);*/
		cin >> n;
		for (i = 0; i < n; ++i) cin >> x[i] >> y[i];
		for (i = 0, m = 0; i < n; ++i)
				for (j = i + 1; j < n; ++j)
				{
						if (j != i)
						{
								for (k = 0, u = 0, d = 0; k < n; ++k)
										if (k != i && k != j)
										{
												s = x[j] * y[k] + x[k]*y[i] + x[i] * y[j] - y[i] * x[j] - y[j] * x[k] - y[k] * x[i];
												if (s < 0) d = max(abs(s), d);
												else u = max(s, u);
										}
								if (u > 0 && d > 0)
										m = m > (u + d) ? m : u + d;
						}
				}
		printf("%.10lf\n", m / 2.0);
		return 0;
}
C:

C是一道数学题,仔细分析一下,就能写出表达式,关键是表达式中有一项是这样的,求题中这条直线上所有点两两距离的和,这当然可以O(n^2),但是显然会TLE,稍微做分析就可以写出表达式是一个与n以及系数有关的表达式,可以在o(n)内解出,于是就水水过了。(Ps, 不知道有没有比O(n)还快的方法,欢迎评论,指点)。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const int N = 100005;
using namespace std;

LL a[N], s[N];

LL gcd(LL a, LL b) {return a % b == 0 ? b : gcd(b, a % b);}

int main()
{
		LL re, n, m, i, j, k, sum = 0, up, down, d;
		scanf("%I64d", &n);
		for (i = 1; i <= n; ++i) scanf("%I64d", a + i);
		sort(a + 1, a + 1 + n);
		for (i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i];
		for (i = 1; i < n; ++i) 
				sum += (n - i) * a[n - i + 1] - (n - i) * a[i];
		up = sum * 2 + s[n];
		down = n;
		d = gcd(up, down);
		up /= d;
		down /= d;
		cout << up << ' ' << down << endl;
		return 0;
}
D:

个人觉得D是一道好题,模拟冒泡排序过程然后结合了图论,我当时又有畏惧心理,没敢仔细想,其实题解中的Lemma1还是能想到的,至于Lemma2就觉得需要一些creation了,我是没想到。但是知道了以后也很简单了。题意就是冒泡排序过程(过程进行到所有数排好序)中,有逆序对,则在对应点之间连边。我们很容想明白的'是,原序列中所有逆序对的数之间肯定都连了边,然后题目要求的是最大的点集的元素个数,使得集合中任意两点间没有连边。这是什么意思呢?满足这个要求的集合是什么呢?Lemma2说的就是这个集合,这个集合的元素是元序列的最长上升子序列,知道结论后回过头看,我们可以证明:首先显然上升子序列在的任意两个元素,是不会连边的。对于某一个上升子序列,能不能添加其他元素而满足题目要求呢?不可能。我们任取一个元素不属于原来的那个上升子序列,为什么它不是这个序列中的元素呢?因为肯定有某个元素和他是逆序关系,不然的话直接添加到序列头或者尾显然能得到一个新的上升子序列。这样就证明了结论。题目中要求集合元素个数最多,当然就是求LIS问题啦。这是可以DP O(n^2)解决的,但是更快的是O(nlogn),具体实现需要二分查找,当然STL中有现成的lower_bound,为什么不用呢?

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const int N = 100005;
using namespace std;

LL a[N], b[N];

int main()
{
		LL re, n, m, i, j, k, en, y;
		ios :: sync_with_stdio(false);
		cin >> n;
		for (i = 0; i < n; ++i) cin >> a[i];
		b[0] = a[0];
		for (i = 1, en = 1; i < n; ++i)
		{
				y = lower_bound(b, b + en, a[i]) - b;
				b[y] = a[i];
				if (y == en) en++;
		}
		cout << en << endl;
		return 0;
}
顺便说一句LISnlogn算法不仅可以求长度,加些改进可以求出一组符合长度最长的上升子序列。这题需要再吃透的就是如何自己发现问题的本质是LIS,这是需要自己不断学习,总结,最后加上一些创造性的。

E:

E是经典的错排,唯一不同就是给定了其中几个点的位置,但仍然可以用容斥远离解决,蛋疼菊紧的是比赛时写的容斥原理是用DFS实现的,也怪pretest太水,没把我的代码跳出来,结果最后测试TLE了(主要受以前一道数论题影响,其实那种容斥原理只适合数据规模很小的情况,但在数论中每个数的素因子个数非常少,整型数据,不同素因子个数肯定不超过20个,所以很适用)这题数据大至2000,用DFS实现,时间复杂度是O(2^n)显然TLE,其实很容易写出容斥原理的公式,最后O(n)直接求解,设k是所有当前状态下可能使 a[i] == i 成立的 i 的个数,F 是所有还没被填上去的数的个数, 那么答案就是:


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const int N = 2005;
using namespace std;

LL a[N], s[N], dead[N], c[N] = {1};
LL fac[N] = {1, 1};

// 原来写的DFS TLE T……T
//LL dfs(LL cnt, LL x, LL y)
//{
//		int i, j, k, ans = 0;
//		if (x == 0) return 1;
//		ans = (ans + fac[x]) % Mod;
//		for (i = y; i < cnt; ++i)
//				if (s[re[i]] == 0)
//				{
//						ans = (ans - dfs(cnt, x - 1, i + 1)) % Mod;
//						if (ans < 0) ans += Mod;
//				}
//		return ans;
//}

LL qp(LL a, LL b)
{
		LL re =1;
		while (b)
		{
				if (b & 1) re = re * a % Mod;
				b >>= 1;
				a = a * a % Mod;
		}
		return re;
}

int main()
{
		LL n, m, i, j, k, cnt, num = 0, re = 0;
		for (i = 2; i <= N; ++i) fac[i] = fac[i - 1] * i % Mod;
		cin >> n;
		for (i = 1, cnt = 0; i <= n; ++i) 
		{
				scanf("%I64d", a + i);
				if (a[i] == -1) cnt++;
				else dead[a[i]] = 1, s[i] = 1;
		}
		for (i = 1, k = 0; i <= n; ++i) if (dead[i] == 0 && s[i] == 0) k++;
		for (i = 1; i <= k; ++i) c[i] = (c[i - 1] * (k - i + 1)) % Mod * qp(i, Mod - 2) % Mod;
		for (i = 0; i <= k; ++i) 
		{
				if (i & 1)
				{
						re = (re - fac[cnt - i] * c[i] % Mod) % Mod;
						if (re < 0) re += Mod;
				}
				else 
						re = (re + fac[cnt - i] * c[i] % Mod) % Mod;
		}
		cout << re <<endl;
		return 0;
}
E貌似比赛时很多人DP做的,不甚了解如何实现,有想法请评论,指教。

我还是滚回去再反思一下D吧。。。怎么样发现问题的本质是自己需要提高的地方。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值