1. 程式人生 > >2017年浙江中醫藥大學程式設計競賽 Solution

2017年浙江中醫藥大學程式設計競賽 Solution

訓練地址

A:

樹剖板子題

求最小值的時候要注意值是不是有負數,如果有,初值要置為$-INF$

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3  
  4 #define N 30010
  5 #define INF 0x3f3f3f3f
  6 int n, q, arr[N];
  7 vector <int> G[N];
  8  
  9 int fa[N], deep[N], sze[N], son[N], top[N], p[N], fp[N], cnt;
 10 void
DFS(int u) 11 { 12 sze[u] = 1; 13 for (auto v : G[u]) if (v != fa[u]) 14 { 15 fa[v] = u; 16 deep[v] = deep[u] + 1; 17 DFS(v); 18 sze[u] += sze[v]; 19 if (!son[u] || sze[v] > sze[son[u]]) son[u] = v; 20 } 21 } 22 23 void getpos(int
u, int sp) 24 { 25 top[u] = sp; 26 p[u] = ++cnt; 27 fp[cnt] = u; 28 if (!son[u]) return; 29 getpos(son[u], sp); 30 for (auto v : G[u]) if (v != fa[u] && v != son[u]) 31 getpos(v, v); 32 } 33 34 namespace SEG 35 { 36 struct node 37 { 38
int Max, sum; 39 node () {} 40 node (int Max, int sum) : Max(Max), sum(sum) {} 41 node operator + (const node &other) const { return node(max(Max, other.Max), sum + other.sum); } 42 }a[N << 2], res; 43 void build(int id, int l, int r) 44 { 45 if (l == r) 46 { 47 a[id] = node(arr[fp[l]], arr[fp[l]]); 48 return; 49 } 50 int mid = (l + r) >> 1; 51 build(id << 1, l, mid); 52 build(id << 1 | 1, mid + 1, r); 53 a[id] = a[id << 1] + a[id << 1 | 1]; 54 } 55 void update(int id, int l, int r, int pos, int val) 56 { 57 if (l == r) 58 { 59 a[id] = node(val, val); 60 return; 61 } 62 int mid = (l + r) >> 1; 63 if (pos <= mid) update(id << 1, l, mid, pos, val); 64 else update(id << 1 | 1, mid + 1, r, pos, val); 65 a[id] = a[id << 1] + a[id << 1 | 1]; 66 } 67 void query(int id, int l, int r, int ql, int qr) 68 { 69 if (l >= ql && r <= qr) 70 { 71 res = res + a[id]; 72 return; 73 } 74 int mid = (l + r) >> 1; 75 if (ql <= mid) query(id << 1, l, mid, ql, qr); 76 if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr); 77 } 78 } 79 80 void query(int u, int v) 81 { 82 while (top[u] != top[v]) 83 { 84 if (deep[top[u]] < deep[top[v]]) swap(u, v); 85 SEG::query(1, 1, n, p[top[u]], p[u]); 86 u = fa[top[u]]; 87 } 88 if (deep[u] > deep[v]) swap(u, v); 89 SEG::query(1, 1, n, p[u], p[v]); 90 } 91 92 int main() 93 { 94 while (scanf("%d%d", &n, &q) != EOF) 95 { 96 cnt = 0; 97 memset(son, 0, sizeof son); 98 for (int i = 1; i <= n; ++i) scanf("%d", arr + i), G[i].clear(); 99 for (int i = 1, u, v; i < n; ++i) 100 { 101 scanf("%d%d", &u, &v); 102 G[u].push_back(v); 103 G[v].push_back(u); 104 } 105 DFS(1); getpos(1, 1); SEG::build(1, 1, n); 106 for (int i = 1, op, x, y; i <= q; ++i) 107 { 108 scanf("%d%d%d", &op, &x, &y); 109 if (op == 2) SEG::update(1, 1, n, p[x], y); 110 else 111 { 112 SEG::res = SEG::node(-INF, 0); 113 query(x, y); 114 printf("%d\n", op == 0 ? SEG::res.Max : SEG::res.sum); 115 } 116 } 117 } 118 return 0; 119 }
View Code

 

B:

$b^2 = 2 \cdot a \cdot (a + 1) ^ 2$

$b = \sqrt{2 \cdot a} \cdot (a +1)$

所以a一定是個平方數的兩倍

即$a = 2 \cdot i^2 $

枚舉出答案,再二分查詢

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 #define ull unsigned long long
 5 #define N 6000020
 6 ull res[N];
 7  
 8 int main()
 9 {
10     for (int i = 0; i < N; ++i)
11         res[i] = (ull)2 * i * ((ull)2 * i * i + 1);
12     //cout << res[N - 1] << endl;
13     int t; cin >> t;
14     while (t--)
15     {
16         ull n; scanf("%llu", &n);
17         int pos = lower_bound(res, res + N, n) - res;
18         printf("%llu\n", res[pos]); 
19     }
20     return 0;
21 }
View Code

 

C:

$倒著推過來$

$對於'u' 記錄一下最近的位置$

$對於'm',記錄一下離他最近的'u'的位置$

$對於'c' , 記錄一下離(離他最近的'm'的)最近的'z'的位置$

$對於'z',可以直接求答案$

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

 

D:

貪心

考慮所有物品都要放進去

那麼肯定先放$a_i <= b_i的物品,因為這些物品放進去揹包會擴容$

那麼放這些物品的時候按照$a_i從小到大的順序排放$

$因為如果小的都放不下,那麼大的肯定放不下,但是大的可以等小的放進去擴容之後可能就可以放下$

那麼剩下的物品按照$(a_i - b_i)從小到大的順序排放$

$因為我們要放一件物品要儘量使得揹包縮小的體積較小,這樣對後面物品的影響也較小$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3   
 4 #define ll long long
 5 #define N 100010
 6 int t, n; ll v;
 7 struct node
 8 {
 9     int a, b;
10     node () {}
11     node (int a, int b) : a(a), b(b) {}
12 };
13 vector <node> p, q;
14   
15 bool ok()
16 {
17     for (auto it : p)
18     {
19         if (v < it.a) return false;
20         v += it.b - it.a;
21     }
22     for (auto it : q)
23     {
24         if (v < it.a) return false;
25         v += it.b - it.a;
26     }
27     return true;
28 }
29   
30 int main()
31 {
32     scanf("%d", &t);
33     while (t--)
34     {
35         scanf("%d%lld", &n, &v);
36         p.clear(), q.clear();
37         for (int i = 1, a, b; i <= n; ++i)
38         {
39             scanf("%d%d", &a, &b);
40             if (a <= b) p.emplace_back(a, b);
41             else q.emplace_back(a, b);
42         }
43         sort(p.begin(), p.end(), [](node x, node y) { return x.a < y.a; });
44         sort(q.begin(), q.end(), [](node x, node y) { return x.a - x.b > y.a - y.b; });
45         puts(ok() ? "yes" : "no");
46     }
47     return 0;
48 }
View Code

 

E:

簽到。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 #define ll long long
 5 int t;
 6 ll n, k;
 7  
 8 int main()
 9 {
10     scanf("%d", &t);
11     while (t--)
12     {
13         scanf("%lld%lld", &n, &k);
14         printf("%lld\n", k);
15     }
16     return 0;
17 }
View Code

 

F:

題意:

給出一個迴圈節,求這個迴圈節上的哪些位置,使得從這個位置出發

不論到達哪個位置,字首1的個數都大於字首0的個數

思路:

剛開始的想法是線段樹的$O(nlogn)做法$

維護處字首0的個數和字首1的個數,然後列舉起始位置,兩個起始位置之間$O(logn)轉移,T了$

然後又考慮了單調佇列的做法,$維護一個lazy, O(n),過了$

最後看了看別人的程式碼,答案是$1的個數-0的個數,暈了$

感覺,首先複雜度沒有演算法,很明顯的需要線性做法,卻要硬剛帶$log的$

再考慮一下,答案為什麼是$1的個數-0的個數$

首先,所有0所處的位置都不可能作為起始位置

再考慮,有哪些1是不可以的

我們考慮連續的0,如果有一段連續的$x個0,那麼這x個0往前數x個1,這x個1都是不可以的$

因為肯定至少存在一個位置不滿足要求

$再考慮離散的0,那麼離散的0直接理解為連續的1個0即可$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 #define N 200010
 5 int t, n, a[N], sum[N], dq[N];
 6  
 7 int main()
 8 {
 9     scanf("%d", &t);
10     while (t--)
11     {
12         scanf("%d", &n);
13         for (int i = 1; i <= n; ++i)
14         {
15             scanf("%d", a + i);
16             a[i + n] = a[i];
17         }
18         int l = 1, r = 0;
19         for (int i = 1, tmp = 0; i <= n; ++i)
20         {
21             tmp += a[i];
22             sum[i] = 2 * tmp - i;
23             while (l <= r && sum[dq[r]] > sum[i]) --r;
24             dq[++r] = i;   
25         }
26         int res = 0, lazy = 0;
27         for (int i = 1; i <= n; ++i)
28         {
29             while (l <= r && dq[l] < i) ++l;
30             if (sum[dq[l]] + lazy > 0) ++res;
31             sum[i + n] = sum[i + n - 1] + 1 * (a[i + n] ? 1 : -1);
32             while (l <= r && sum[dq[r]] > sum[i + n]) --r;
33             lazy += 1 * (a[i] ? -1 : 1);
34             dq[++r] = i + n;
35         }
36         printf("%d\n", res);
37     }
38     return 0;
39 }
View Code

 

G:

簽到。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 int main()
 5 {
 6     int t; cin >> t;
 7     while (t--)
 8     {
 9         long long n, m;
10         scanf("%lld%lld", &n, &m);
11         printf("%lld\n", m - n);
12     }
13     return 0;
14 }
View Code