Wannafly挑战赛28 Solution

A:msc和mas

Solved.

考虑斐波那契数列,即最多加45次即会超过1e9,直接暴力即可

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 int a, b, l;
 5  
 6 int solve(int st)
 7 {
 8     int A = a, B = b;
 9     while (1)
10     {
11         if (st == 1)
12         {
13             if (A > l) return printf("Yes");
14             else
15             {
16                 int B0 = B;
17                 while (B < 2 * B0) B += A;
18             }
19         }
20         else
21         {
22             if (B > l) return printf("No");
23             else
24             {
25                 int A0 = A;
26                 while (A < 2 * A0) A += B;
27             }
28         }
29         st ^= 1;
30     }
31 }
32  
33 int main()
34 {
35     while (scanf("%d%d%d", &a, &b, &l) != EOF)
36     {
37         solve(1); putchar(' ');
38         solve(0); putchar('\n');
39     }
40     return 0;
41 }
View Code

 

B:msc和mcc

Solved.

考虑如果有一个区间满足,那么左右两边扩展区间肯定也是满足的

再考虑从左到右,如果$1-x满足,那么2-y满足的话, 显然有y >= x$

那么只需要双指针找到以i为左界,找到最近的右界,那么这个点的贡献就是n - r + 1

再考虑怎么判断是否合法

不难发现,只有mscmcc 和 mccmsc 两种情况,注意其中可以加入其他字符,但是最终取出的子序列都会归结到这两种情况 都判断一下即可

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define N 100010
 6 int n; 
 7 char str[N];
 8 set <int> m, s, c;
 9 int st_m[2], pos_s, pos_c[2];
10 
11 bool check1()
12 {
13     // mscmcc
14     if (s.upper_bound(st_m[0]) == s.end()) return false;
15     pos_s = *s.upper_bound(st_m[0]); 
16     if (c.upper_bound(pos_s) == c.end()) return false;
17     pos_c[0] = *c.upper_bound(pos_s);
18     if (c.upper_bound(max(st_m[1], pos_c[0])) == c.end()) return false;
19     pos_c[1] = *c.upper_bound(max(st_m[1], pos_c[0]));
20     if (c.upper_bound(pos_c[1]) == c.end()) return false;
21     return true; 
22 }
23 
24 bool check2()
25 {
26     // mccmsc
27     if (c.upper_bound(st_m[0]) == c.end()) return false;
28     pos_c[0] = *c.upper_bound(st_m[0]);
29     if (c.upper_bound(pos_c[0]) == c.end()) return false;
30     pos_c[1] = *c.upper_bound(pos_c[0]);
31     if (s.upper_bound(st_m[1]) == s.end()) return false;
32     pos_s = *s.upper_bound(st_m[1]);
33     if (c.upper_bound(max(pos_s, pos_c[1])) == c.end()) return false; 
34     return true;
35 }
36     
37 bool ok()
38 {
39     if (m.size() < 2) return false;
40     if (s.size() < 1) return false;
41     if (c.size() < 3) return false;
42     st_m[0] = *m.begin(); m.erase(m.begin()); 
43     st_m[1] = *m.begin(); m.insert(st_m[0]); 
44     if (check1()) return true;
45     if (check2()) return true;
46     return false; 
47 }
48 
49 int main()
50 {
51     while (scanf("%d", &n) != EOF)
52     {
53         m.clear(); s.clear(); c.clear();
54         ll res = 0; 
55         scanf("%s", str + 1);
56         for (int i = 1, r = 0; i <= n; ++i)
57         {
58             while (r < n && !ok())
59             {
60                 ++r;  
61                 if (str[r] == 'm') m.insert(r);
62                 else if (str[r] == 's') s.insert(r);
63                 else c.insert(r); 
64             }
65             if (!ok()) break;
66             res += n - r + 1; 
67             if (str[i] == 'm') m.erase(i);
68             else if (str[i] == 's') s.erase(i);
69             else c.erase(i); 
70         }
71         printf("%lld\n", res); 
72     }
73     return 0;
74 }
View Code

 

C:msc的宠物

Upsolved.

先二分答案,再DP求解最少需要删去的边数。

设$g[u] 表示 以 u 为根的子树下最少需要删去的边数,f[u][x] 表示以u为根的子树下u所处的连通块中点权最大值为x的情况下最少需要删去的边数$

$对于u 和 v  如果 a[u] < x 并且 a[v] < x 并且 abs(a[u] - a[v])  < x  那么 u 和 v 就可以同属一个连通块 $

$那么转移就是 f[u][x] += min(g[v] +1, f[v][x])$

其他情况的转移都是 $f[u][x] += g[v] +1$

最后 $g[u] = min(f[u][x])$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 1010
 5 #define INF 0x3f3f3f3f
 6 #define ll long long
 7 int n, k, a[N], f[N][N], g[N]; ll x;
 8 vector <int> G[N];
 9 
10 void dp(int u, int fa = 0)
11 {
12     for (auto v : G[u]) if (v != fa)
13     {
14         dp(v, u);
15         for (int i = 1; i <= n; ++i)
16         { 
17             if (a[u] <= a[i] && a[v] <= a[i] && abs(a[u] - a[v]) <= x) 
18                 f[u][i] += min(g[v] + 1, f[v][i]); 
19             else
20                 f[u][i] += g[v] + 1;
21         }
22     }
23     for (int i = 1; i <= n; ++i)
24         g[u] = min(g[u], f[u][i]);
25 }
26 
27 bool check()
28 {
29     memset(g, 0x3f, sizeof g);
30     for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j)
31         f[i][j] = (abs(a[i] - a[j]) <= x) ? 0 : INF;
32     dp(1);  
33     return g[1] <= k;
34 }
35 
36 void Run()
37 {
38     while (scanf("%d%d", &n, &k) != EOF)
39     {
40         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
41         for (int i = 1, u, v; i < n; ++i)
42         {
43             scanf("%d%d", &u, &v);
44             G[u].push_back(v);
45             G[v].push_back(u);
46         } 
47         ll l = 0, r = 2000000000, res = -1;
48         while (r - l >= 0)
49         {
50             x = (l + r) >> 1; 
51             if (check())
52             {
53                 res = x;
54                 r = x - 1;
55             }
56             else
57                 l = x + 1;
58         }
59         printf("%d\n", res);
60     }
61 }
62 
63 int main()
64 {
65     #ifdef LOCAL
66         freopen("Test.in", "r", stdin);
67     #endif 
68 
69     Run();
70     return 0;
71 }
View Code

 

D:msc的背包

Unsolved.

 

E:msc的序列

Unsolved.

 

F:msc的无向图

Unsolved.

 

转载于:https://www.cnblogs.com/Dup4/p/9937585.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值