Codeforces Round #538 (Div. 2)

 

A. Got Any Grapes?

签.

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int x, y, z, a, b, c;
 5 bool solve()
 6 {
 7     if (a < x) return false;
 8     a -= x;
 9     b += a;
10     if (b < y) return false;
11     b -= y;
12     c += b;
13     if (c < z) return false;
14     return true;
15 }
16 
17 int main()
18 {
19     while (scanf("%d%d%d", &x, &y, &z) != EOF)
20     {
21         scanf("%d%d%d", &a, &b, &c);
22         puts(solve() ? "YES" : "NO");
23     }
24     return 0;
25 }
View Code

 

 

B. Yet Another Array Partitioning Task

Solved.

题意:

有一个序列, 要求把这个序列分成$k段,每段最少m个$

要求每一段中的前$m$大的数加起来的总和最大

输出总和以及分配的方案

注意这里分的序列是连续的

思路:

我们考虑答案的总和就是所有数排序后的前$m * k大之和$

$我们考虑一个不属于前m * k大的数$

$首先我们考虑每一段中都有m个数$

$并且这m个数属于前m * k大的$

$那么剩下的数,无论分到哪个组,都不会被统计进答案$

$那么只要贪心选取,标记哪些数是前m * k大$

$每个组只要够了m个这样的数,就开启下一组$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define N 200010
 6 int n, m, k;
 7 int vis[N];
 8 struct node
 9 {
10     int a, id;
11     void scan(int id)
12     {
13         this->id = id;
14         scanf("%d", &a);
15     }
16     bool operator < (const node &other) const
17     {
18         return a > other.a;
19     }
20 }a[N];
21 
22 int main()
23 {
24     while (scanf("%d%d%d", &n, &m, &k) != EOF)
25     {
26         memset(vis, 0, sizeof vis);
27         for (int i = 1; i <= n; ++i) a[i].scan(i);
28         ll res = 0;
29         sort(a + 1, a + 1 + n); 
30         for (int i = 1; i <= m * k; ++i) res += a[i].a, vis[a[i].id] = 1;
31         vector <int> vec;
32         for (int i = 1, tmp = 0; i <= n; ++i)
33         {
34             tmp += vis[i];
35             if (tmp == m)
36             {
37                 vec.push_back(i);
38                 tmp = 0;
39             }
40         }
41         printf("%lld\n", res);    
42         for (int i = 0; i < k - 1; ++i)
43             printf("%d%c", vec[i], " \n"[i == k - 2]);
44     }
45     return 0;
46 }
View Code

 

 

C. Trailing Loves (or L'oeufs?)

Solved.

题意:

求$n!在b进制下末尾的0的个数$

思路:

我们考虑什么时候会产生$0$

对于$b进制,我们知道满b进1,末尾添一个0$

$那么我们只需要知道n!中所有数因数分解之后可以凑成多少个b末尾就是多少个0$

我们令$b = p_1^{t_1} \cdot p_2^{t_2} \cdots p_n^{t_n}$

那么求出$n!中有多少个 p_1^{t_1}, p_2^{t_2} \cdots p_n^{t_n} 取Min即可$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll unsigned long long
 5 #define pll pair <ll, ll>
 6 ll n, b;
 7 
 8 ll f()
 9 {
10     ll res = (ll)1e18;
11     ll limit = sqrt(b);  
12     vector <pll> vec;
13     for (ll i = 2; i <= limit; ++i) if (b % i == 0)  
14     {
15         pll tmp = pll(i, 0);
16         while (b % i == 0)
17         {
18             ++tmp.second;
19             b /= i; 
20         }
21         vec.push_back(tmp);
22     } 
23     if (b != 1)   
24         vec.push_back(pll(b, 1));
25     for (auto it : vec)
26     {
27         ll tmp = 0;
28         for (ll i = it.first; ; i *= it.first) 
29         {
30             tmp += n / i;
31             if (i > n / it.first + 10) break;
32         }
33         res = min(res, tmp / it.second);
34     }
35     return res;   
36 }
37 
38 int main()
39 {
40     while (scanf("%llu%llu", &n, &b) != EOF)
41     {
42         printf("%llu\n", f());
43     }
44     return 0;
45 }
View Code

 

 

D. Flood Fill

Upsolved.

题意:

$有一行不同颜色的球$

$首先可以选择一个主球,相同的球会合并$

$在接下来的每一步,我们都可以规定主球为任一颜色$

$和它相邻的并且颜色和它相同的都会合并$

$求所有球合并完的最少步骤$

思路:

 

$DP解法$

$dp[l][r][0/1] 表示 l->r 这个区间合并完后状态为0/1的最小步数$

$0表示合并后的颜色和c[l]相同,1表示合并后的颜色和c[1]相同$

$那么dp[l][r][0/1] 可以转移到 dp[l - 1][r][0]$

$dp[l][r][0/1] 可以转移到 dp[l][r + 1][1]$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 5010
 5 int n, c[N];
 6 int f[N][N][2];
 7 
 8 int dp(int l, int r, int sta)
 9 {
10     if (l >= r)
11         return 0;
12     if (f[l][r][sta] != -1) 
13         return f[l][r][sta];
14     int res = (int)1e8;
15     if (sta == 0)
16     {
17         res = min(res, dp(l + 1, r, 0) + (c[l] != c[l + 1]));
18         res = min(res, dp(l + 1, r, 1) + (c[l] != c[r]));
19     }
20     else
21     {
22         res = min(res, dp(l, r - 1, 0) + (c[r] != c[l]));
23         res = min(res, dp(l, r - 1, 1) + (c[r] != c[r - 1]));
24     }
25     return f[l][r][sta] = res;
26 }
27 
28 int main()
29 {
30     while (scanf("%d", &n) != EOF)
31     {
32         for (int i = 1; i <= n; ++i) scanf("%d", c + i);
33         memset(f, -1, sizeof f);
34         printf("%d\n", min(dp(1, n, 0), dp(1, n, 1)));
35     }
36     return 0;
37 }
View Code

 

$LCS解法$

我们假设$主球的位置在x$

$那么x把区间分成左右两部分$

$每次可以选择左边一个合并或者右边一个合并$

$但是更优的是 左边和右边的相同,那么一步就可以将他们都合并$

$我们就是要找这样相同的$

假如这样的东西

$abcxcba$

$x代表主球,相同字母代表相同数字$

$那么我们只需要三次就可以合并完$

$我们要找的就是这种相同的越多越好,并且是有顺序可以接在一起的$

$那么把字符串翻转求LCS就可以$

$要注意除2 ,因为多算了一次$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 5010
 5 int n, m, a[N], b[N];
 6 int f[N][N];
 7 
 8 int main()
 9 {
10     while (scanf("%d", &n) != EOF)
11     {
12         a[0] = -1; m = 0;
13         for (int i = 1, x; i <= n; ++i)
14         {
15             scanf("%d", &x);
16             if (x != a[m]) a[++m] = x;
17         }
18         for (int i = 1; i <= m; ++i) 
19             b[m - i + 1] = a[i];
20         memset(f, 0, sizeof f);
21         for (int i = 1; i <= m; ++i)
22             for (int j = 1; j <= m; ++j)
23             {
24                 f[i][j] = max(f[i][j - 1], f[i - 1][j]);  
25                 if (a[i] == b[j]) 
26                     f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
27             }
28         printf("%d\n", m - 1 - f[m][m] / 2);
29     }
30     return 0;
31 }
View Code

 

下面这个线段树版本本质上和$LCS思想差不多$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 10010  
 5 int n, m, c[N];
 6 vector <int> vec[N];
 7 
 8 namespace SEG
 9 {
10     int Max[N << 2];
11     void init(){ memset(Max, 0, sizeof Max); }
12     void update(int id, int l, int r, int pos, int val)
13     {
14         if (l == r)
15         {
16             Max[id] = max(Max[id], val);
17             return;
18         }
19         int mid = (l + r) >> 1;
20         if (pos <= mid) update(id << 1, l, mid, pos, val);
21         else update(id << 1 | 1, mid + 1, r, pos, val);
22         Max[id] = max(Max[id << 1], Max[id << 1 | 1]);
23     }
24     int query(int id, int l, int r, int ql, int qr)
25     {
26         if (qr < ql) return 0;
27         if (l >= ql && r <= qr) return Max[id];
28         int res = 0;
29         int mid = (l + r) >> 1;
30         if (ql <= mid) res = max(res, query(id << 1, l, mid, ql, qr));
31         if (qr > mid) res = max(res, query(id << 1 | 1, mid + 1, r, ql, qr));
32         return res;
33     }
34 }
35     
36 int main()
37 {
38     while (scanf("%d", &n) != EOF)
39     {
40         m = 0, c[0] = -1;
41         for (int i = 1, x; i <= n; ++i)
42         {
43             scanf("%d", &x);
44             if (x != c[m]) c[++m] = x;
45         }
46         for (int i = m + 1; i <= 2 * m; ++i) c[i] = c[i - m]; 
47         for (int i = 1; i <= m; ++i) vec[i].clear();  
48         for (int i = 2 * m; i >= m + 1; --i) vec[c[i]].push_back(i);
49         int res = (int)1e8;
50           SEG::init();
51         for (int i = m; i >= 1; --i)
52         {
53             for (auto it : vec[c[i]])
54             {
55                 int tmp = SEG::query(1, 1, 2 * m, m + 1, it - 1);
56                 SEG::update(1, 1, 2 * m, it, tmp + 1);
57             }
58         }    
59         for (int i = m; i <= 2 * m; ++i) res = min(res, m - 1 - SEG::query(1, 1, 2 * m, i, i) / 2);
60         printf("%d\n", res);
61     }
62     return 0;
63 }
View Code

 

 

E. Arithmetic Progression

Upsolved.

题意:

给出一个等差序列,但是不有序

$有两种询问方式$

$?\; i 表示询问第i个位置上的数是多少$

$> x 表示询问序列中存不存在严格大于x的数字$

最后给出首项和公差

询问次数最多60

思路:

首先可以用$30次询问求出上界$

$接着如果搞定公差下界就有了$

$我们随机询问30个数,就得到900个差值$

$再求这个差值的gcd就是答案$

$为什么不会出现求的值的答案的倍数的情况?$

$概率很小,官方题解有证 (逃$

注意随机的时候要用大数随机

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define N 1000010
 6 int n, cnt;
 7 
 8 bool check(int x)
 9 {
10     printf("> %d\n", x);
11     ++cnt;
12     fflush(stdout);
13     int tmp;
14     scanf("%d", &tmp);
15     return tmp == 0; 
16 }
17 
18 int vis[N];
19 int gcd(int a, int b)
20 {
21     return b ? gcd(b, a % b) : a;
22 }
23 
24 int main()
25 {
26     while (scanf("%d", &n) != EOF)
27     {
28         if (n <= 60)
29         {
30             vector <int> vec;
31             for (int i = 1, x; i <= n; ++i)
32             {
33                 printf("? %d\n", i);
34                 fflush(stdout);
35                 scanf("%d", &x);
36                 vec.push_back(x);
37             }
38             sort(vec.begin(), vec.end());
39             printf("! %d %d\n", vec[0], vec[1] - vec[0]);
40             fflush(stdout);
41             continue;
42         }
43         cnt = 0;
44         int l = 0, r = (int)1e9, res = -1;
45         while (r - l >= 0)
46         {
47             int mid = (l + r) >> 1;
48             if (check(mid))
49             {
50                 res = mid;
51                 r = mid - 1;
52             }
53             else
54                 l = mid + 1;
55         }
56         vector <int> vec, gap; 
57         vec.push_back(res);
58         memset(vis, 0, sizeof vis); 
59         for (int i = cnt + 1, x; i <= 60; ++i)
60         {
61             do
62             {
63                 x = ((rand() << 14) + rand()) % n + 1;
64             } while (vis[x]);
65             vis[x] = 1;
66             printf("? %d\n", x);
67             fflush(stdout); 
68             scanf("%d", &x);
69             vec.push_back(x); 
70         }
71         sort(vec.begin(), vec.end());
72         vec.erase(unique(vec.begin(), vec.end()), vec.end());
73         for (int i = 0, len = vec.size(); i < len; ++i)
74             for (int j = i + 1; j < len; ++j)
75                 gap.push_back(vec[j] - vec[i]);
76         int d = 0;
77         for (auto it : gap) d = gcd(d, it);
78         printf("! %d %d\n", res - (n - 1) * d, d);
79         fflush(stdout);
80     }
81     return 0;
82 }
View Code

 

F. Please, another Queries on Array?

Upsolved.

题意:

给出一个序列

有两种操作

$M\; l, r, x 将[l, r]里面的数乘上x$

$q\; l, r  询问\phi(\prod_{i = l}^{r} a_i) mod 10^9 + 7$

思路:

考虑$\phi(p^k) = p^{k - 1} \cdot (p - 1)$

并且$\phi()是积性函数$

$有\phi(x \cdot y) = \phi(x) \cdot \phi(y)$

$那么令k = p_1^{t_1} \cdot p_2^{t_2} \cdots p_n^{t_n}$

那么

$\phi(k) = \phi(p_1^{t_1}) \cdots$

$\phi(k) = p_1^{t_1 - 1} \cdot (p_1 - 1) \cdots$

$\phi(k) = p_1^{t_1} \cdot \frac{p_1 - 1}{p_1} \cdots$

 

那么询问的答案就是

$\phi(k) = k \cdot \prod_{p \in P} \frac{p - 1}{p}$

 

前面那一段用乘积线段树维护

考虑题目的数的值中涉及到的素数只有62个

$后面那一段用或线段树维护是否存在即可$

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 #define ll long long
  5 #define N 400010
  6 const ll MOD = (ll)1e9 + 7;  
  7 int n, m, q, a[N];
  8 int prime[110], pos[310];    
  9 ll inv[110], b[310];
 10 
 11 void init()
 12 {
 13     m = 0; 
 14     memset(pos, 0, sizeof pos);
 15     for (int i = 2; i <= 300; ++i)
 16     {
 17         if (!pos[i])
 18             prime[++m] = i;
 19         for (int j = i * i; j <= 300; j += i)  
 20             pos[j] = 1;
 21     }
 22     for (int i = 1; i <= 300; ++i)
 23     {
 24         b[i] = 0;
 25         for (int j = 1; j <= 62; ++j)
 26             if (i % prime[j] == 0)
 27                 b[i] |= (1ll << (j - 1));  
 28     }
 29 }
 30 
 31 ll qmod(ll base, ll n)
 32 {
 33     ll res = 1;
 34     while (n)
 35     {
 36         if (n & 1) res = res * base % MOD;
 37         base = base * base % MOD;
 38         n >>= 1;
 39     }
 40     return res;
 41 }
 42 
 43 namespace SEG
 44 {
 45     struct node
 46     { 
 47         int cnt;  
 48         ll sum, lazy; 
 49         ll S, W;
 50         void init() 
 51         {
 52             sum = 1;  
 53             lazy = 1;
 54             S = 0, W = 0;
 55         }
 56         void add(ll val, ll SE)
 57         {
 58             sum = (sum * qmod(val, cnt)) % MOD; 
 59             lazy = (lazy * val) % MOD;   
 60             S |= SE;
 61             W |= SE;
 62         } 
 63         node operator + (const node &other) const
 64         {
 65             node res; res.init(); 
 66             res.sum = (sum * other.sum) % MOD;
 67             res.S = S | other.S;
 68             res.cnt = cnt + other.cnt;
 69             return res;  
 70         }
 71     }a[N << 2], res;  
 72     void pushdown(int id)
 73     {
 74         if (a[id].lazy == 1 && a[id].W == 0) return;
 75         a[id << 1].add(a[id].lazy, a[id].W);
 76         a[id << 1 | 1].add(a[id].lazy, a[id].W);
 77         a[id].lazy = 1;    
 78         a[id].W = 0;
 79     } 
 80     void build(int id, int l, int r)
 81     {
 82         a[id].init();
 83         a[id].cnt = (r - l + 1);
 84         if (l == r) return;
 85         int mid = (l + r) >> 1;
 86         build(id << 1, l, mid);
 87         build(id << 1 | 1, mid + 1, r);
 88     }
 89     void update(int id, int l, int r, int ql, int qr, ll val, ll SE)
 90     {
 91         if (l >= ql && r <= qr)
 92         {
 93             a[id].add(val, SE);
 94             return;
 95         }
 96         int mid = (l + r) >> 1;
 97         pushdown(id);
 98         if (ql <= mid) update(id << 1, l, mid, ql, qr, val, SE);
 99         if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, val, SE);
100         a[id] = a[id << 1] + a[id << 1 | 1];
101     }
102     void query(int id, int l, int r, int ql, int qr)
103     {
104         if (l >= ql && r <= qr)
105         {
106             res = res + a[id];
107             return;
108         }
109         int mid = (l + r) >> 1;
110         pushdown(id);
111         if (ql <= mid) query(id << 1, l, mid, ql, qr);
112         if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr);
113     }
114 }
115 
116 int main()
117 {
118     init();
119     for (int i = 1; i <= 62; ++i)
120         inv[i] = qmod(prime[i], MOD - 2); 
121     while (scanf("%d%d", &n, &q) != EOF)
122     {
123         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
124         SEG::build(1, 1, n);
125         for (int i = 1; i <= n; ++i)
126             SEG::update(1, 1, n, i, i, a[i], b[a[i]]);
127         char op[20]; int l, r, x;
128         for (int i = 1; i <= q; ++i)
129         {
130             scanf("%s%d%d", op, &l, &r);
131             if (op[0] == 'M')
132             {
133                 scanf("%d", &x); 
134                 SEG::update(1, 1, n, l, r, x, b[x]);
135             }
136             else
137             {
138                 SEG::res.init();
139                 SEG::query(1, 1, n, l, r);
140                 ll res = SEG::res.sum;
141                 ll S = SEG::res.S;
142                 for (int j = 1; j <= 62; ++j)
143                     if ((S >> (j - 1)) & 1ll)
144                         res = (res * (prime[j] - 1) % MOD * inv[j]) % MOD;
145                 printf("%lld\n", res);
146             }
147         }
148     }
149     return 0;
150 }
View Code

 

posted @ 2019-02-11 16:32  Dup4  阅读(272)  评论(0编辑  收藏  举报