Codeforces Round #198 (Div. 2)
A.The Wall
题意:两个人粉刷墙壁,甲从粉刷标号为x,2x,3x...的小块乙粉刷标号为y,2y,3y...的小块问在某个区间内被重复粉刷的小块的个数。
分析:求出x和y的最小公倍数,然后做一个一维的区间减法就可以了。
#include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; int x, y, a, b; int main() { scanf("%d %d %d %d", &x, &y, &a, &b); int d = __gcd(x, y); int lcm = x / d * y; printf("%d\n", b/lcm - (a-1)/lcm); return 0; }
题意:给定若干个点,求任选四个点后组成的最大的多边形面积。
分析:不能够枚举四个点的组合情况,可以把任何一个四边形看作是一条直线作为边的两个三角形组成的,这样两边的三角形分别求最值就可以了,时间复杂度O(n^3)。
#include <cstdio> #include <iostream> #include <cmath> #include <algorithm> using namespace std; int n, x[305], y[305]; const int inf=1<<25; int main( ) { scanf("%d", &n); for( int i=0; i<n; ++i ){ scanf("%d%d", &x[i], &y[i]); } int Max=0, m1, m2, n1, n2,t; for( int i=0; i<n; ++ i ){ for( int j=i+1; j<n; ++ j ){ m1=-inf, m2=-inf, n1=inf, n2=inf; for( int k=0; k<n; ++ k ){ if(k==i || k==j) continue; t=(x[j]-x[i])*(y[k]-y[i]) - (x[k]-x[i])*(y[j]-y[i]); if(t<0){ m1=max(m1, -t ); n1=min(n1, -t); } else{ m2=max(m2, t); n2=min(m2, t); } } Max=max(Max, m1+m2); } } printf("%.6lf", 1.0*Max/2); return 0; }
题意:给定一个N个点在一维直线的坐标,现在有按照 N! 种不同的排列方式来访问这些点,问平均每步的距离为多少,定义两位置的距离为坐标差的绝对值。
分析:单独考虑每个元素在所有排列中的总贡献。对于每一个元素只考虑其前一个元素和其的距离,那么一共有N-1个元素与其匹配,对于每个数该数对存在的位置共有N-1种,加之剩下的数的排列数(N-2)!则每个数对共有(N-1)!种情况,计算出任何一个数与其余N-1个数的绝对值之差之和,然后乘上(N-2)!就是总路程和了。由于要求的平均距离,因此还应除以N!,刚好 (N-1)! 和 N! 的可以约掉。
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int N = 100005; typedef long long LL; int seq[N]; LL sum[N]; int n; int main() { scanf("%d", &n); LL a = 0, b = 0; for (int i = 1; i <= n; ++i) { scanf("%d", &seq[i]); } sort(seq+1, seq+1+n); for (int i = 1; i <= n; ++i) { sum[i] = sum[i-1] + seq[i]; } for (int i = 1; i <= n; ++i) { a += 1LL*(i-1)*seq[i]-sum[i-1]+ sum[n]-sum[i-1]-1LL*(n-i)*seq[i]; } b = n; LL d = __gcd(a, b); a /= d, b /= d; printf("%I64d %I64d\n", a, b); return 0; }
题意:给定一个1-N的全排列,定义逆序对为ai > aj when i < j。对于所有的逆序对连边,求最后形成图的最大独立子集。
分析:初看起来确实是无从下手,但是可以发现一个性质那就是所有的最后的独立子集一定会是原序列中一个单调非递减的子序列,因此求最长单调非递减子序列即可。
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int N = 100005; int seq[N]; int n; typedef vector<int> v_int; void solve() { v_int vt; v_int::iterator it; for (int i = 0; i < n; ++i) { it = upper_bound(vt.begin(), vt.end(), seq[i]); if (it == vt.end()) vt.push_back(seq[i]); else *it = seq[i]; } printf("%d\n", (int)vt.size()); } int main() { scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%d", &seq[i]); } solve(); return 0; }
题意:给定一个1-N的全排列,不过有些位置是被-1代替的,现在要求可能的原序列有多少种,方案数 mod 10^9+7。题目给定一个限制那就是 ai != i。
分析:由于 ai != i 这完全符合错排的定义,不过稍有不同的此处已经给出了某些元素的位置,那么考虑到错排运用容斥定理的推理过程可知此处正好能够应对这一变种。根据输入统计出有多少个数字的安排是有限制的,多少是没有限制的。然后枚举有限制的数字放在限制位置的个数,容斥一下即可。
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int N = 2005; const int mod = int(1e9)+7; int n, x, y; // x表示自由放置的数字的数量,y表示有限制的数字 char hav[N]; char fre[N]; int fac[N]; void pre() { fac[0] = 1; for (int i = 1; i <= 2000; ++i) { fac[i] = 1LL*fac[i-1]*i%mod; } } int pow(int a, int b) { int ret = 1; while (b) { if (b & 1) ret = 1LL*ret*a%mod; b >>= 1; a = 1LL*a*a%mod; } return ret; } void solve() { int ret = 0; for (int i = 0; i <= y; ++i) { LL sign = i & 1 ? -1 : 1; ret = (1LL*ret+sign*fac[x+y-i]*pow(1LL*fac[i]*fac[y-i]%mod, mod-2)%mod)%mod; ret = (ret + mod) % mod; } printf("%d\n", 1LL*ret*fac[y]%mod); } int main() { pre(); int a; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", &a); if (a != -1) hav[a] = 1, fre[i] = 1; } for (int i = 1; i <= n; ++i) { if (!hav[i]) { if (fre[i]) ++x; else ++y; } } solve(); return 0; }