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 }
T1

 

统计
【问题描述】
给定长度为𝑛的序列𝑎。记𝐴 = {𝑎<, 𝑎=,…, 𝑎>},你需要计算下面的表达式的值:
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 }
T2

 

 

 

 

旅行
【问题描述】
给定一个长度为𝑛的序列𝑎<, 𝑎=,…, 𝑎>。定义序列的代价为𝑎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 }
T3

 

posted @ 2017-09-22 21:33  嘒彼小星  阅读(343)  评论(0编辑  收藏  举报