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

 

H:

题意:

将一个n * n矩形分成两个相同的部分,求方案数

思路:

n 为奇数 答案为0

然后从中间开始搜,对称标记

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 int t, ans[11], res, n;
 5 int used[20][20];
 6 int Move[][2] =
 7 {
 8     0, -1,
 9     0,  1,
10     1,  0,
11    -1,  0,
12 };
13  
14 void DFS(int x, int y)
15 {
16     if (x == 0 || y == 0 || x == n || y == n)
17     {
18         ++res;
19         return;
20     }
21     for (int i = 0; i < 4; ++i)
22     {
23         int nx = x + Move[i][0];
24         int ny = y + Move[i][1];
25         if (used[nx][ny] == 0)
26         {
27             used[nx][ny] = 1;
28             used[n - nx][n - ny] = 1;
29             DFS(nx, ny);
30             used[nx][ny] = 0;
31             used[n - nx][n - ny] = 0;
32         }
33     }
34 }
35  
36  
37 int main()
38 {
39     for (int i = 1; i < 10; ++i)
40     {
41         if (i & 1) ans[i] = 0;
42         else
43         {
44             memset(used, 0, sizeof used);
45             res = 0; n = i;
46             used[i / 2][i / 2] = 1;
47             DFS(i / 2, i / 2);
48             ans[i] = res / 4;
49         }
50     }
51     scanf("%d", &t);
52     while (t--)
53     {
54         scanf("%d", &n);
55         printf("%d\n", ans[n]);
56     }
57     return 0;
58 }
View Code

 

I:

按题意模拟即可。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3  
  4 #define N 10010
  5 int t, n;
  6 string str;
  7 string var[N], save[N];
  8 int Size[N], Len[N];
  9 map <string, int> mp;
 10  
 11 void getName(int i, int len)
 12 {
 13     string name = "";
 14     for (; i < len; ++i)
 15     {
 16         if (str[i] == '[')
 17         {
 18             ++i;
 19             break;
 20         }
 21         name += str[i];
 22     }
 23     int big = 0;
 24     for (; i < len; ++i)
 25     {
 26         if (str[i] == ']') break;
 27         big = big * 10 + str[i] - '0';
 28     }
 29     ++n;
 30     mp[name] = n;
 31     var[n] = name;
 32     save[n] = "";
 33     Size[n] = big;
 34     Len[n] = 0;
 35 }
 36  
 37 int main()
 38 {
 39     ios::sync_with_stdio(false);
 40     cin.tie(0); cout.tie(0);
 41     cin >> t; getline(cin, str);
 42     while (t--)
 43     {
 44         n = 0;
 45         mp.clear();
 46         while (1)
 47         {
 48             getline(cin, str);
 49             if (str == "return 0;") break;
 50             if (str[0] == 'c' && str[1] == 'h')
 51             {  
 52                 for (int i = 0, len = str.size(); i < len; ++i) if (str[i] == ' ')
 53                     getName(i + 1, len);               
 54             }
 55             else if (str[0] == 'g')
 56             {
 57                 string name = "";
 58                 str += "\n";
 59                 int i = 0, len = str.size();
 60                 for (; i < len; ++i) if (str[i] == ' ') 
 61                 {
 62                     ++i;
 63                     break;
 64                 }
 65                 for (; i < len; ++i)
 66                 {
 67                     if (str[i] == ' ')
 68                     {
 69                         ++i;
 70                         break;
 71                     }
 72                     name += str[i];
 73                 }          
 74                 int id = mp[name];
 75                 Len[id] = len - i; 
 76                 string s = "";
 77                 for (int j = 0; i < len && j < Size[id]; ++i, ++j)
 78                     s += str[i];
 79                 save[id] = s;
 80             }
 81             else
 82             {
 83                 string name = "";
 84                 int i = 0, len = str.size();
 85                 for (; i < len; ++i) if (str[i] == ' ')
 86                 {
 87                     ++i;
 88                     break;
 89                 }
 90                 for (; i < len; ++i) name += str[i];
 91                 int id = mp[name];
 92                 string res = save[id];
 93                 int remind = Len[id] - Size[id];
 94                 for (int i = id + 1; remind > 0 && i <= n; ++i)
 95                 {
 96                     res += save[i];
 97                     remind -= Size[i] - Len[i]; 
 98                 }
 99                 if (res.end()[-1] != '\n') res += "\n";
100                 cout << res;
101             }
102         }
103     }
104     return 0;
105 }
View Code

 

 

j:

考虑如果没有$gcd(x, y) != 1 的限制$

那么直接处理处b[a[i]]

再对于$a[b[i]] 直接求答案$

有这个限制我们可以考虑容斥,可以直接用莫比乌斯函数来容斥

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define N 100010
 6 int n;
 7 int a[N], b[N];
 8 bool check[N]; int mu[N], prime[N];
 9 void Moblus()
10 {
11     memset(check, false, sizeof check);
12     mu[1] = 1;
13     int tot = 0;
14     for (int i = 2; i < N; ++i)
15     {
16         if (!check[i])
17         {
18             prime[++tot] = i;
19             mu[i] = -1;
20         }
21         for (int j = 1; j <= tot; ++j)
22         {
23             if (i * prime[j] >= N) break;
24             check[i * prime[j]] = true;
25             if (i % prime[j] == 0)
26             {
27                 mu[i * prime[j]] = 0;
28                 break;
29             }
30             else
31             {
32                 mu[i * prime[j]] = -mu[i];
33             }
34         }
35     }
36 }
37 
38 ll vis[N];
39 ll f(int t)
40 {
41     ll res = 0;
42     for (int i = t; i <= n; i += t) ++vis[b[a[i]]];
43     for (int i = t; i <= n; i += t) res += vis[a[b[i]]];
44     for (int i = t; i <= n; i += t) --vis[b[a[i]]];
45     return res;
46 }
47 
48 int main()
49 {
50     Moblus();
51     while (scanf("%d", &n) != EOF)
52     {
53         memset(vis, 0, sizeof vis);
54         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
55         for (int i = 1; i <= n; ++i) scanf("%d", b + i);
56         ll res = 0;
57         for (int i = 1; i <= n; ++i)
58             res += mu[i] * f(i);
59         printf("%lld\n", res);        
60     }
61     return 0;
62 }
View Code

 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值