NOIP模拟17.9.22
NOIP模拟17.9.22
前进!
【问题描述】
数轴的原点上有一只青蛙。青蛙要跳到数轴上≥ 𝐷的位置去,但很不幸数轴
上有𝑛个区间是禁区,不能进入。青蛙会选择一个长度𝐿,从原点开始每次向右
跳长度为𝐿的一段。一路上青蛙会停的位置是0, 𝐿, 2𝐿,…直到跳到了≥ 𝐷的位置,
任意一个位置都不能在禁区中。请求出𝐿的最小值,注意𝐿可以是实数。
【输入格式】
输入文件为susume.in。
输入文件的第一行包含两个整数𝑛和𝐷,含义如问题描述中所述。
接下来𝑛行,每行描述一个禁区。每行有两个整数𝑙和𝑟,代表数轴上(𝑙, 𝑟)的
区间是禁区。保证有0 ≤ 𝑙 < 𝑟 ≤ 𝐷。
【输出格式】
输出文件为susume.out。
输出一行,代表𝐿的最小值。精度要求请参考评分方法。
【样例输入1】
2 25
11 21
3 7
【样例输出1】
10.5
【样例1 解释】
第一步跳到了𝑥 = 10.5,第二步跳到𝑥 = 21,第三步就跳到了≥ 𝐷的位置。
可以证明这个解是最小的解。
【样例输入2】
2 100
0 50
50 100
【样例输出2】
50.0
【题解】
考场上想出正解,但是因为SPJ所以没测
不难证明一定会在某次跳跃跳到某个区间的右端点
这样我们枚举是哪一个右端点,不断/2,/3,/4.../r作为
步长L。然后在On时间判断是否合法即可
挂标称了
1 //#line 7 "TheFrog.cpp" 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 #include <cmath> 6 #include <climits> 7 #include <algorithm> 8 #include <vector> 9 #include <string> 10 #include <iostream> 11 #include <map> 12 #include <queue> 13 #include <utility> 14 #include <ctime> 15 #include <sstream> 16 using namespace std; 17 18 typedef long long ll; 19 #define pair(x, y) make_pair(x, y) 20 21 #define N 50 22 23 class TheFrog { 24 public: 25 static const double eps = 1e-9; 26 27 pair<double, double> p[N + 1]; 28 int n, D_; 29 double ans, m; 30 31 inline bool check(double x) { 32 for (int i = 1; i <= n; ++i) { 33 int ds = (int)(p[i].first / x + eps); 34 if (x * (double)(ds + 1) < p[i].second - eps) 35 return false; 36 } 37 return true; 38 } 39 40 double getMinimum(int D, vector <int> L, vector <int> R) { 41 ans = 1e10, m = 1e10, D_ = D; 42 n = L.size(); 43 for (int i = 1; i <= n; ++i) p[i] = pair((double)L[i - 1], (double)R[i - 1]); 44 for (int i = 0; i < n; ++i) m = min(m, (double)R[i] - L[i]); 45 sort(p + 1, p + n + 1); 46 for (int i = 1; i <= n; ++i) { 47 double x = p[i].second; 48 int div = (int)(x / ans); 49 while (x / (double)div > m - eps) { 50 if (check(x / (double)div)) ans = min(ans, x / (double)div); 51 ++div; 52 } 53 } 54 return ans; 55 } 56 }; 57 58 int n, D; 59 vector <int> L, R; 60 61 int main() { 62 freopen("susume.in", "r", stdin); 63 freopen("susume.out", "w", stdout); 64 65 TheFrog solution; 66 scanf("%d%d", &n, &D); 67 for (int i = 1; i <= n; ++i) { 68 int l, r; 69 scanf("%d%d", &l, &r); 70 L.push_back(l), R.push_back(r); 71 } 72 73 double ans = solution.getMinimum(D, L, R); 74 printf("%.10lf\n", ans); 75 76 fclose(stdin); 77 fclose(stdout); 78 return 0; 79 }
统计
【问题描述】
给定长度为𝑛的序列𝑎。记𝐴 = {𝑎<, 𝑎=,…, 𝑎>},你需要计算下面的表达式的值:
max
C∈E
𝑥 − min
I∈E
𝑦
E⊆L
E MN
说得通俗一点,就是序列𝑎的元素构成了集合𝐴(序列𝑎中值相同的多个元素
在𝐴中也被视为不同的元素),求𝐴的所有大小为𝑘的子集中,元素的最大值减最
小值,之和。
由于答案可能很大,输出答案对𝑝取模之后得到的结果。
【输入格式】
输入数据的第一行包含三个整数𝑛、𝑘和𝑝。
接下来一行,包含𝑛个整数,代表序列𝑎。
【输出格式】
输出一行,包含一个整数,即为答案。
【样例】
setdiff.in setdiff.out
4 2 10007
10 20 30 40
100
【样例解释】
一共有6 个可选的子集:
• {10, 20},最大值为20,最小值为10;
• {10, 30},最大值为30,最小值为10;
• {10, 40},最大值为40,最小值为10;
• {20, 30},最大值为30,最小值为20;
• 20, 40 ,最大值为40,最小值为20;
• {30, 40},最大值为40,最小值为30。
【数据规模和约定】
对于30%的数据,𝑛 ≤ 20。
对于60%的数据,𝑛 ≤ 5000。
对于100%的数据,1 ≤ 𝑘 ≤ 𝑛 ≤ 100000,0 ≤ 𝑎R ≤ 106,2 ≤ 𝑝 ≤ 106 + 7且
𝑝为质数。
【题解】
这个题的那个绝对值太迷惑了,去掉绝对值肯定知道
把式子化一下啊,真是蠢啊。。。。。。。。。
排个序,组合算一下就好了
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 #define max(a, b) ((a) > (b) ? (a) : (b)) 7 #define min(a, b) ((a) < (b) ? (a) : (b)) 8 9 inline void read(long long &x) 10 { 11 x = 0;char ch = getchar(), c = ch; 12 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 13 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 14 if(c == '-')x = -x; 15 } 16 17 const int INF = 0x3f3f3f3f; 18 const int MAXN = 100000 + 10; 19 20 long long n, k, p, num[MAXN], ans, f[MAXN]; 21 22 long long pow(long long a, long long b) 23 { 24 long long r = 1, base = a % p; 25 for(;b;b >>= 1) 26 { 27 if(b & 1) r *= base, r %= p; 28 base *= base, base %= p; 29 } 30 return r; 31 } 32 33 inline long long ni(long long num) 34 { 35 return pow(num, p - 2); 36 } 37 38 long long C(long long n, long long m) 39 { 40 if(n < m)return 0; 41 return ((f[n] * ni(f[m]))%p * ni(f[n - m]))%p; 42 } 43 44 int main() 45 { 46 read(n), read(k), read(p); 47 f[0] = 1; 48 for(register int i = 1;i <= n;++ i) 49 f[i] = f[i - 1] * i, f[i] %= p; 50 for(register int i = 1;i <= n;++ i) 51 read(num[i]); 52 std::sort(num + 1, num + 1 + n); 53 for(register int i = k;i <= n;++ i) 54 ans += C(i - 1, k - 1) * (num[i] % p) % p; 55 std::sort(num + 1, num + 1 + n, std::greater<int>()); 56 for(register int i = k;i <= n;++ i) 57 ans -= C(i - 1, k - 1) * (num[i] % p) % p, ans = (ans%p + p)%p; 58 printf("%d", ans); 59 return 0; 60 }
旅行
【问题描述】
给定一个长度为𝑛的序列𝑎<, 𝑎=,…, 𝑎>。定义序列的代价为𝑎R − 𝑎RU<
>5<
RM< 。
你现在可以任意次交换相邻的两个数,但是除了第一次交换以外,每次交换
的两个数的位置都应该在前一次交换的两个数的位置的右边。
比如说,第一次交换了(𝑎=, 𝑎7)这两个数,第二次就不能交换(𝑎<, 𝑎=)或者
(𝑎=, 𝑎7),但是可以交换(𝑎7, 𝑎V)或者(𝑎V, 𝑎W)等。
求任意交换之后序列的最小代价。
【输入格式】
输入数据第一行一个整数𝑛。
接下来一行包含𝑛个整数,代表数列的每个元素。
【输出格式】
输出一行,代表最小代价。
【样例输入】
4
2 3 4 1
【样例输出】
4
【样例解释】
第一次,交换(𝑎<, 𝑎=),数列变成{3,2,4,1}。
第二次,交换(𝑎=, 𝑎7),数列变成{3,4,2,1}。
可以证明这是最优方案。
【数据规模和约定】
对于10%的数据,𝑛 ≤ 10。
对于20%的数据,𝑛 ≤ 18。
对于50%的数据,𝑛 ≤ 100。
对于100%的数据,𝑛 ≤ 2000,𝑎R ≤ 100000。
【题解】
DP神题。
我们把i标位1意味着i与i + 1交换
i标位0意味着i与i + 1不交换
我们发现如果两个1之间夹着0,那么这两个1互不影响
如果是连续的一串1,起点l,终点r,由于必须从左向右
进行,其实相当于做了一次循环移动,变成了l,l+1,l+2.。。。r-1,r,l
于是我们在序列上做DP
因为是i与i + 1交换,i可以从i + 1转移过来,
为了方便计算, 我们用倒推。
dp[i][0]表示i这个位置放1,即第i个数与第i + 1个数交换,从i...n的最小代价
dp[i][1]表示i这个位置放0,即第i个数与第i + 1个数不交换,从i...n的最小代价
转移的时,0比较显然,1我们就枚举连续1的长度即可
有很多细节,转移详见代码
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #define max(a, b) ((a) > (b) ? (a) : (b)) 6 #define min(a, b) ((a) < (b) ? (a) : (b)) 7 #define abs(a) ((a) < 0 ? (-1 * (a)) : (a)) 8 9 inline void read(int &x) 10 { 11 x = 0;char ch = getchar(), c = ch; 12 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 13 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 14 if(c == '-')x = -x; 15 } 16 17 const int INF = 0x7fffffff; 18 const int MAXN = 10000 + 10; 19 20 int dp[MAXN][2], sum[MAXN], num[MAXN], n; 21 22 int main() 23 { 24 read(n); 25 for(register int i = 1;i <= n;++ i) 26 read(num[i]); 27 for(register int i = 2;i <= n;++ i) 28 sum[i] = sum[i - 1] + abs(num[i] - num[i - 1]); 29 for(register int i = n - 1;i >= 1;-- i) 30 { 31 if(i == n - 1) 32 dp[i][0] = abs(num[i + 1] - num[i]); 33 else 34 dp[i][0] = min(dp[i + 1][0] + abs(num[i] - num[i + 1]), 35 dp[i + 1][1] + abs(num[i] - num[i + 2])); 36 dp[i][1] = INF; 37 for(register int j = i + 1;j < n - 1;++ j) 38 { 39 dp[i][1] = min(dp[i][1], 40 abs(num[i] - num[j]) + sum[j] - sum[i + 1] + 41 min(dp[j + 1][0] + abs(num[i] - num[j + 1]), 42 dp[j + 1][1] + abs(num[i] - num[j + 2]))); 43 } 44 if(i <= n - 2) 45 dp[i][1] = min(dp[i][1], 46 sum[n - 1] - sum[i + 1] + abs(num[i] - num[n - 1]) + abs(num[i] - num[n])); 47 dp[i][1] = min(dp[i][1], 48 sum[n] - sum[i + 1] + abs(num[n] - num[i])); 49 } 50 printf("%d", min(dp[1][0], dp[1][1])); 51 return 0; 52 }