[比賽][ICPC2020] 2020ICPC小米網絡選拔賽第一場


一、前言

聽說今年難得有場邀請賽,而且還是在北京線下,感覺是個大好的機會 —— 畢竟上次參加南昌場的邀請賽直接奪銀,感覺狀態再好點沖個金也沒問題。然而事實告訴我這並不是場一如既往的邀請賽,43 個名額實在少得可憐,而且學校似乎並沒有名額限制,以至於清北直接屠榜了,不知道是今年情況特殊還是這場比賽特殊。最后勉為其難寫了 5 道題,距離入圍依舊遙不可及。

 

二、比賽概況

1、比賽鏈接

https://ac.nowcoder.com/acm/contest/7501

2、題目情況

A 題(AC,已填坑):數論 + 動態規划

B 題(已填坑):計算幾何 + 最短路

C 題(AC,已填坑):模擬

D 題(AC):圖論

E 題:略

F 題:貪心 + 二分答案

G 題:略

H 題:略

I 題(AC):搜索(BFS)+ 圖論

J 題(AC):二維前綴和 + 二維差分

K 題:略

3、得分情況

AC 了 A, C, D, I, J 五道,終榜 rank300+。

 

三、題目分析

A. Intelligent Warehouse

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211807

2、題面

In MI Intelligent Warehouse, there are nn products, where the i-th product is of size ai. We always need to box producsts into all kinds of containers, and it will be safer and more cost efficient if for any two sizes of products, one is the other's multiple, since there won't be any residual room in one container. So for each boxing we need to choose some products that for any two chosen products, either ai is multiple of aj or aj is multiple of ai. Print the maximum number of products that can be chosen in one boxing.

3、題目大意

給出數列 a,從中選出盡可能多的數,使任意兩個數之間存在倍數關系。

4、分析與思路

很早就開了這道題,首先確定了幾個事情:

① 數的順序沒有意義,故可以預處理為從小到大排序;

② 任意兩個數均有倍數關系,考慮 b 是 a 的倍數,如果 c 是 a 的倍數,則 c 必然也是 a 的倍數,集合 a, b, c 也就滿足條件;故對於數列 s,從小到大排序后,任意相鄰的兩個元素存在倍數關系時,數列的任意兩個元素均存在倍數關系;

③ 根據第 ② 條,考慮動態規划毋庸置疑,f[i] 表示 “最大值為 i 時從數列中最多選出的數的個數能夠滿足條件”。舉個例子,對於數列 {1, 2, 3, 4, 8},f[1] = 1, f[2] = 2, f[3] = 2, f[4] = 3, f[8] = 4。

根據上述分析,提供三種算法:

① O(w * sqrt(w)) 枚舉約數(TLE)

首先想到的是 O(w * sqrt(w)) 的做法。值域只有 1e7,則可以預處理出某個數是否在數列中,然后枚舉數列中每個數的因數是否存在於該數列,存在則轉移,交了一發 TLE 了;

然后想到,枚舉因數的這個過程存在太多冗余的判斷,比如假設 b 是 a 的約數, c 是 b 的約數,則恆存在 f[a] >= f[b] >= f[c],在找到 b 這個 a 的約數並進行 f[a] = max(f[a], f[b] + 1) 的轉移后,f[a] = max(f[a], f[c] + 1) 的判斷則沒有任何意義。

② O(w log w) 枚舉倍數(評測機抖動可 A 可 T)

結束后看了眼 A 題的最短代碼 300+ byte,屬實震驚,細細品讀后發現是發現就是直接枚舉所有倍數關系。二隊其實想到了這個但寫 WA 了,分析之后覺得必然會 T 於是也沒再寫下去。然而我們將這個 O(w log w) 的算法交了若干次竟然每次結果都不同,可能這就是評測機抖動⑧。這也太看臉了。誰又敢把自己已經 T 了的代碼再交一次呢?

③ O(w log log w) 線性篩 + 枚舉倍數(AC,正解)

在方法 ② 的基礎上進行改進 —— 使用 Eratosthenes 篩法(請參見 6.4.1  素數與最大公約數,預處理出所有值域范圍內的質數,從 1 至值域最大值枚舉,如果當前數 i 存在於數列,則 f[i]++,然后枚舉該數的所有質數倍數 j,構成倍數關系即可進行狀態轉移 f[j] = f[i]。

上述表述還有一定問題,題目並沒有說數列中不存在重復數,故我們並非是預處理數是否在數列中,而是出現次數。事后重寫的時候被這里卡了無數次,淦。

④ O(w log sqrt(w)) 線性篩 + 枚舉約數(AC)

其實枚舉約數和枚舉倍數可以看做兩個相反的過程,對於本題的數據,枚舉倍數顯然優於枚舉約數;加上線性篩后,兩者復雜度均可接受。思路和方法 ① 是一樣的,相對麻煩一點,略過不提。

5、題解

設Dp[i]表示當前選的所有數都是i的約數且符合題意的情況下能選的數的個數的最大值。最后答案就是所有Dp值中的最大值。

一個非常直觀的轉移就是用Dp[i]去更新i的所有倍數的Dp值,但是這樣復雜度是O(W log W)的,可能會TLE(但實際上跑得和正解一樣快)。實際上只用枚舉i的素數倍就可以了,因為合數總可以被若干個素數的乘積給湊出來,就沒必要再枚舉了,復雜度和埃拉托斯特尼篩法的復雜度是一樣的。

時間復雜度:O(W log log W)

(出題人給出的題解 pdf 加密了,所以是通過 OCR 復制下來的)

6、代碼

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int MAXM = 1e7 + 5;
 5 const int MAXN = 2e5 + 5;
 6 
 7 int n, mx, x, o, in[MAXM], p[MAXM], s[MAXM], f[MAXM], ans;
 8 
 9 int main() {
10     cin >> n;
11     p[1] = 1;
12     for (int i = 1; i <= n; i++)
13         cin >> x, in[x]++, mx = max(x, mx);
14     for (int i = 2; i <= mx; i++) {
15         if (!p[i]) s[o++] = i;
16         for (int j = 0; j < o && i * s[j] <= mx; j++) {
17             p[i * s[j]] = 1;
18             if (i % s[j] == 0) break;
19         }
20     }
21     for (int i = 1; i <= mx; i++) {
22         f[i] += in[i];
23         for (int j = 0; j < o && i * s[j] <= mx; j++)
24             f[i * s[j]] = max(f[i * s[j]], f[i]);
25         ans = max(ans, f[i]);
26     }
27     cout << ans;
28     return 0;
29 }

 

B. Intelligent Robot

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211808

2、題面

Given a maze of size n×m, whose lower-left corner is (0, 0)​ while upper-right corner is (n, m). And there are k​ segment walls in the maze. One cannot cross the walls but can touch them or walk along them. Now a MI robot is assigned to go from (X1​, Y1​) to (X2, Y2), as an intelligent robot, MI robot will automatically determine the shortest valid path and go along it. Print the length of the shortest valid path between (X1​, Y1​) and (X2​, Y2​).

3、題目大意

給出 n * m 的坐標系,給出 k 條線段,求出從 (0, 0) 到 (n, m) 的路徑,要求不穿過 k 條線段(但允許穿過線段端點或者沿着線段走)。

4、分析與思路

神經刀地想了個方法,其實主要是當時沒題寫了就正好用這道題練練計算幾何的手感,過了個樣例就交了,WA 了也沒覺得可惜,因為根本沒覺得能 A。結束后看分數竟然高達 50,再看題解 —— 這不就是一個意思?今天又看了眼其他隊的 AC 代碼,才發現自己加了個沒用的判斷條件,刪掉那句話就行了。。。。

我的想法是,牆是起到格擋作用的,原本可以直接走起點終點相連的路徑被阻擋了;但是題目允許經過牆的端點,根據最短路原則,存在牆阻擋的情況下,最短路徑必然經過牆的端點,那么我們可以把問題重構為所有牆的端點加上起點終點總共 2 * k + 2 個點的完全圖上的最短路問題,使用 Dijkstra 算法求解即可。

由於是完全圖,邊的數量達到了 k ^ 2。顯然並不是所有端點相連的線段都是可行的邊,如果線段和牆相交,則不能連邊,那么就需要對所有邊都和 k 面牆進行線段相交的判斷,時間復雜度為 O(k ^ 3),算上常數,理論上達到了 (300 * 2) ^ 3 ≈ 2e8 的運算量,可能會 TLE,但也許是數據沒有達到這么極限,所以可行。

5、題解

首先有用的點只有O(k)個,然后問題在於判斷兩點之間是否可以直達,這個就O(k)地枚舉每堵牆看有沒有擋住線路,具體來說就看兩點組成的線段與牆對應的線段師是否嚴格相交。最后再用Dijkstra 算法求最短路即可。
時間復雜度:O(k ^ 3)

6、代碼

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define MAXN 1000005
 5 #define INF 1000005.0
 6 
 7 int n, m, k;
 8 int o, h[MAXN], s1, t1, vis[MAXN], tot;
 9 double dis[MAXN];
10 
11 class Node {
12 public:
13     int o;
14     double d;
15     bool operator < (const Node &o) const {
16         return o.d < d;
17     }
18 };
19 
20 class Edge {
21 public:
22     int v, nxt;
23     double w;
24 } e[MAXN << 1];
25 
26 class Point {
27 public:
28     int x, y;
29     bool operator == (const Point &o) const {
30         return x == o.x && y == o.y;
31     }
32 } p1[MAXN], p2[MAXN], a[MAXN], s, t;
33 
34 priority_queue <Node> q;
35 
36 double mult(Point a, Point b, Point c) {  
37     return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);  
38 }  
39   
40 bool is(Point a, Point b, Point c, Point d) {  
41     return max(a.x, b.x) < min(c.x, d.x) || max(a.y, b.y) < min(c.y, d.y) ||
42         max(c.x, d.x) < min(a.x, b.x) || max(c.y, d.y) < min(a.y, b.y) ||
43         mult(c, b, a) * mult(b, d, a) <= 0 || mult(a, d, c) * mult(d, b, c) <= 0 ? 0 : 1;
44 }  
45 
46 double getDis(Point a, Point b) {
47     return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
48 }
49 
50 void add(int u, int v) {
51     o++, e[o] = (Edge){v, h[u], getDis(a[u], a[v])}, h[u] = o;
52 }
53 
54 void dijkstra() {
55     for (int i = 1; i <= tot; i++)
56         dis[i] = INF;
57     dis[s1] = 0;
58     q.push((Node){s1, 0});
59     Node o;
60     while (!q.empty()) {
61         o = q.top(); q.pop();
62         if (!vis[o.o]) {
63             vis[o.o] = 1;
64             for (int x = h[o.o]; x; x = e[x].nxt) {
65                 int v = e[x].v;
66                 if (dis[v] > o.d + e[x].w)
67                     dis[v] = o.d + e[x].w, q.push((Node){v, dis[v]});
68             }
69         }
70     }
71 }
72 
73 int main() {
74     cin >> n >> m >> k;
75     for (int i = 1; i <= k; i++)
76         cin >> p1[i].x >> p1[i].y >> p2[i].x >> p2[i].y;
77     cin >> s.x >> s.y >> t.x >> t.y;
78     for (int i = 1; i <= k; i++)
79         a[++tot] = p1[i], a[++tot] = p2[i];
80     a[++tot] = s, a[++tot] = t;
81     s1 = tot - 1, t1 = tot;
82     for (int i = 1; i < tot; i++)
83         for (int j = i + 1; j <= tot; j++) {
84             int nob = 0;
85             for (int l = 1; l <= k; l++) {
86                 if (a[i] == p1[l] && a[j] == p2[l]) continue;
87                 if (is(a[i], a[j], p1[l], p2[l])) {
88                     nob = 1;
89                     break;
90                 }
91             }
92             if (!nob) add(i, j), add(j, i);
93         }
94     dijkstra();
95     cout << fixed << setprecision(4) << dis[t1];
96     return 0;
97 }

 

C. Smart Browser

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211809

2、題面

In some social platforms, there are some netizen who like to post "www". Since the string is like the grass, which contains plenty of "/\" shapes, so there are also some netizen who post "grass".

As a fast, smart, and convenient browser, MI Browser can recognize this kind of text and show some special effects. Specifically, given a string, MI Browser will determine the number of "/\" shapes made by character "w", where "w" itself and the gap between two adjacent "w"s both give one `/\` shape. If still not clear, please turn to sample input/output for more details.

As a MI fan, you want to detect the relationship between the special effects and the number of "/\" shapes, so the thing you should do first is to determine the number of "/\" shapes by yourself.

3、題目大意

給出一個字符串,求出若干個由連續的 w 組成的子串。

4、分析與思路

簽到題。1 個 w(“w”)有 1 個 “/\”,2 個 w(“ww”)有 3 個 “/\”,因為字符相連處還有一個,所以 n 個 w 有 2 * n - 1 個 w,那么我們找出所有由連續的 “w” 組成的子串,並求 “/\” 的總和即可。

開始沒太認真看題,結合樣例里出現的 “v”,還以為只要是構成了 “/\” 就都算,浪費了太多時間。

5、出題人題解

按照題意模擬即可。

6、代碼

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3  
 4 #define MAXN 100005
 5  
 6 int l, o = 1, tot, ans;
 7 char a[MAXN];
 8  
 9 int main() {
10     cin >> a + 1;
11     l = strlen(a + 1);
12     while (o <= l) {
13         if (a[o] == 'w') {
14             tot = 0;
15             while (a[o] == 'w') o++, tot++;
16             ans += tot * 2 - 1;
17         }  
18         o++;
19     }
20     cout << ans;
21     return 0;
22 }

 

D. Router Mesh 

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211810

2、題面

In a Mesh networking system, there are nn MI Routers, where mm pairs of MI Routers are bidirectionally connected. In order to check the stability of the system, for each MI Router, we should determine the number of connected components after removing it and relative connections from the system. And then assess that if we should add more MI Routers into the system. Print the numbers of connected components in residual system after removing each MI Router.

3、題目大意

給出由 n 個點,m 條邊組成的非連通無向圖,求出每次刪除其中一個點后的連通塊個數。

4、分析與思路

5、題解

這題乍一眼看上去像是個時間分治+可撤銷並查集(按秩合並)的數據結構題,但復雜度是O(m+nlog2n)的,可能會TLE。
實際上這題有圖論做法,只需要對每個點i求出其所在的點雙連通分量的個數ci,當然也要求出初始的連通塊個數C,那么i的答案就是C+ci - 1。
時間復雜度:O(n+m)

6、代碼

 1 #include <bits/stdc++.h>
 2  
 3 using namespace std;
 4 typedef long long ll;
 5 typedef unsigned long long ull;
 6 const int N = 3e5 + 50;
 7 const int NN = 2e5 + 50;
 8 const int M = 1e9 + 7;
 9 int n,m;
10 struct Edge{
11     int u,v,nxt,cut;
12 } e[N*2];
13 int cnt = 1, head[N], dfn[N],low[N];
14 int ans[N];
15 int mx = 0;
16 int clk = 0;
17 #define multi_case
18 #undef multi_case
19 void add_edge(int u,int v) {
20     e[++cnt] = (Edge){u,v,head[u],0};
21     head[u] = cnt;
22     e[++cnt] = (Edge){v,u,head[v],0};
23     head[v] = cnt;
24 }
25 void pre() {
26     cin >> n >> m;
27     for(int i = 1; i <= m; i++) {
28         int v,u;
29         cin >> u >> v;
30         add_edge(u,v);
31     }
32 }
33 void Tarjan(int u,int fa) {
34     dfn[u]=low[u]=++clk;
35     ans[u] = 0;
36     int cnt = 0;
37     if(fa == 0) ans[u] = -1;
38     for(int i=head[u];i;i=e[i].nxt) {
39         int v = e[i].v;
40         if(!dfn[v])
41         {
42             cnt++;
43             Tarjan(v,u);
44             low[u]=min(low[v],low[u]);
45             if(fa == 0) {
46                 ans[u] = cnt-1;
47             }
48             else if(fa != 0 && low[v] >= dfn[u])
49                 ans[u]++;
50         }
51         else if(v != fa)
52             low[u]=min(low[u],dfn[v]);
53     }
54 }
55 void solve(int _case) {
56     int block_cnt = 0;
57     for(int i = 1; i <= n; i++) {
58         if(dfn[i] == 0) {
59             Tarjan(i,0);
60             block_cnt++;
61         }
62     }
63     for(int u = 1; u <= n; u++) {
64  
65         printf("%d%c", block_cnt + ans[u], " \n"[u==n]);
66     }
67 }
68 int main() {
69     ios::sync_with_stdio(false);
70     cin.tie(0);
71     int t;
72     pre();
73 #ifdef multi_case
74     cin >> t;
75 #else
76     t = 1;
77 #endif
78     for (int i = 1; i <= t; i++) {
79         solve(i);
80     }
81     return 0;
82 }

(樊總的代碼)

 

E. Phone Network 

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211815

2、題面

In M Country, there are n cities and these cities are placed in one line, where the cities are labeled 1, 2, ⋯, n one by one. Citizens in M Country all use MI-phones, and there are totally m network modes among the MI-phones. For better management, MI-phones in i-th city can only use Ai-th mode.

As a MI fan, you want to experience all kind of network modes. So for each i(i∈{1, 2, ⋯, m}), you want to find a shortest subinterval [Li, Ri] in M Country that all network modes from 1 to i are used at least once among the cities whose labels are in interval [Li, Ri], and we denote li as the length of the shortest subinterval. In this problem, you need to determine l1, l2, ..., lm.

3、題目大意

 

4、分析與思路

5、題解

先考慮維護一個 R[i][l],表示 l 為左端點,包含 1 ~ i 中所有數字的最小右端點。那么當 i 轉移到 i + 1 時,不妨設數字 i + 1 的位置從左到右分別為 p1, p2, …, pk,那么可知對於 [p[j - 1] + 1, p[j]]中的 l,其 R[i + 1][l] = max{R[i][l], p[j]},就變成區間求 max 了。特別地,對於 [ p[k] + 1, n] 的這一段 R[i + 1][l],可以令其變成 +∞。

注意到 R[i][1 ~ n] 是單調的,所以我們可以在 [p[j - 1] + 1, p[j]] 中找到 R[i + 1][l] < p[j] 的一段l,這樣就變成區間賦值了。然后考慮再維護一個 R[i][l] - l + 1 的值,每次取其中的最小值即為答案。

時間復雜度:O(n log n)

6、代碼

 

F. Design Problemset 

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211817

2、題面

In xCPC contest, there are totally k​ types of problems. Now you have aia_iai​ problems in type i​, and you want to make some problemsets with these problems.

In your opinion, the number of problems in one problemset must be in interval [L,R]​, and the number of problems in type i​ must be in interval [li,ri].

Now you want to know the maximum number of problemsets you can make, where one problem can be used in no more than one problemset.

3、題目大意

給出 k 類題,要求組出盡可能多套卷子。每類題有總題數,每套卷子題數的最小值和最大值三種屬性。每套卷子也有總題數的最小值和最大值限制。

4、分析與思路

5、題解

這題可能有個坑點,就是所有題的 li, ri 的限制與 L, R 的限制沖突,即 ∑ri < L 或者 R < l,這樣的話答案自然是 0。
首先一套題的題目個數肯定是越少越好,所以就取 max{L, ∑li} 作為一套題的題目數量 P。然后二分答案,不妨設當前答案為 A,考慮檢驗其合法性。首先看所有種類的題目是否滿足ai ≥ A × li,如果不滿足則肯定就不合法,否則每套題還需要 P - ∑li 道題來充數,總共就需要A × (P - ∑li) 道充數題,然后每種題目可以拿出 min{ai - A × l, A × (ri - li)} 道題來充數,看已有的可充數題是否不少於所需充數題即可。
時間復雜度:O(n log w)。

6、代碼

 

G. Tree Protection 

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211818

2、題面

Given two 1∼n permutations A, B​, you should construct an unrooted tree T​, which satisfies that A​ is a possible topological order of T​ if A1​ is made the root, and that B​ is a possible dfs order of T​ if B1​ is made the root.

Here, a permutation P​ of size n​ is a valid topological order of a rooted tree T​ of size n​ iff that for all edges in T​, the parent nodes are at front of the child nodes in permutation P​.

3、題目大意

 

4、分析與思路

5、題解

做法很多,標程的做法如下(記 PosA[i] 為 i 在 A 中的出現位置):

puts("YES");
for (int i = 2, cur = B[1]; i <= n; i ++) {
    printf("%d %d\n", cur, B[i]);
    if (PosA[B[i]] < PosA[cur])
        cur = B[i];
}

大概是說從左到右枚舉排列 B,對於一個 B[i](i > 1),找到 B[1 ~ i - 1] 中在排列 A 中出現位置最靠前的一個並與其連邊。
可以驗證上述算法運行結果就是一個合法解。

考慮排列 A:對於每一條邊 (cur, B[i]),可知誰在排列 A 中更靠前,誰就離 A[1] 更近,也就是說在 A 中靠前的點是靠后的點的父親。所以當 A[1] 為根時,A 是 T 的一個合法拓撲序。

考慮排列 B:當 B[1] 為根時,對於一個點 B[i],可知要么這個點是葉子節點,要么后面所有的點 B[i + 1 ~ n] 都是這個點的子孫,也就是說每個點的子樹都在排列 B 的某個子區間中。所以當 B[1] 為根時,B 是 T的一個合法 dfs 序。

6、代碼

 

H. Grouping 

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211819

2、題面

Given an integer n and 2n integers a1, a2, ⋯, a2n, you should divide these numbers into n groups and each group contains exactly two integers. Now define the weight of a group as the difference between the two integers in the group, and the weight of a dividing manner as the variance of the n group weights. Determine the expectation value of the dividing manner weight modulo 998244353.

Here, the variance of n numbers a1, a2, ⋯, an equals 1 / n ∑i(a[i] - /a) ^ 2, where /a = 1 / n * ∑i(a[i]).

3、題目大意

 

4、分析與思路

5、題解

6、代碼

 

I. Walking Machine

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211820

2、題面

Given a maze of size n×m, where upper left corner is (1,1) and lower right corner is (n,m). For each cell (x,y), there is exactly one character c (c∈{W,A,S,D}) on it, denoting the moving restriction:

* If c=W, you can only go up from this cell, specifically go from (x,y) to (x−1,y)

* If c=A, you can only go left from this cell, specifically go from (x,y) to (x,y−1)

* If c=S, you can only go down from this cell, specifically go from (x,y) to (x+1,y)

* If c=D, you can only go right from this cell, specifically go from (x,y) to (x,y+1)

We say one is out if x<1 or x>n or y<1 or y>m holds, now you should determine the number of cells that one can be out if start walking on it.

3、題目大意

給出一個 n * m 的矩陣,每一個格子有固定且唯一的方向,求從 (1, 1) 到 (n, m) 的路徑的長度。

4、分析與思路

簽到題 2,暴力搜索,DFS / BFS 均可,fwl 寫的,沒啥好分析的。

5、題解

把圖倒過來建,然后從迷宮外開始 BFS,看能搜到多少個格子即可。時間復雜度:O(nm)

6、代碼

 1 #include <bits/stdc++.h>
 2  
 3 using namespace std;
 4 typedef long long ll;
 5 typedef unsigned long long ull;
 6 const int N = 1000 + 5;
 7 const int M = 1e9 + 7;
 8 int n,m;
 9 char s[N][N];
10 int ans[N][N];
11 #define multi_case
12 #undef multi_case
13 bool out(int i,int j) {
14     return i <= 0 || i > n || j <= 0 || j > m;
15 }
16 bool dfs(int x,int y) {
17     if(out(x,y)) return true;
18     if(ans[x][y] == 1) return true;
19     if(ans[x][y] == 0) return false;
20     ans[x][y] = false;
21  
22     int dx = x, dy = y;
23     if(s[x][y] == 'W') dx--;
24     else if(s[x][y] == 'S') dx++;
25     else if(s[x][y] == 'A') dy--;
26     else if(s[x][y] == 'D') dy++;
27     ans[x][y] = dfs(dx,dy);
28     return ans[x][y];
29 }
30 void pre() {
31     cin >> n >> m;
32     for(int i = 1; i <= n; i++) {
33         cin >> (s[i]+1);
34     }
35     for(int i = 1; i <= n; i++) {
36         for(int j = 1; j <= m; j++) {
37             ans[i][j] = -1;
38         }
39     }
40 }
41 void solve(int _case) {
42     for(int i = 1; i <= n; i++) {
43         for(int j = 1; j <= m; j++) {
44             if(ans[i][j] == -1) dfs(i,j);
45         }
46     }
47     int cnt = 0;
48     for(int i = 1; i <= n; i++) {
49         for(int j = 1; j <= m; j++) {
50             if(ans[i][j]) cnt++;
51         }
52     }
53     printf("%d\n", cnt);
54 }
55 int main() {
56 //    freopen("data.in","r",stdin);
57 //    freopen("data.out","w",stdout);
58     ios::sync_with_stdio(false);
59     cin.tie(0);
60     int t;
61     pre();
62 #ifdef multi_case
63     cin >> t;
64 #else
65     t = 1;
66 #endif
67     for (int i = 1; i <= t; i++) {
68 //        printf("Case #%d: ",i);
69         solve(i);
70     }
71     return 0;
72 }

(樊總的代碼)

 

J. Matrix Subtraction

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211822

2、題面

Given a matrix M of size n×m and two integers a,b, determine weither it is possible to make all entrys of M zero by repeatedly choosing a×b submatrices and reduce the values in the chosen matrices by 1. If possible, print "^_^" in one line, or print "QAQ" in one line.

3、題目大意

給出一個 n * m 的矩陣,每次對一個 a * b 矩陣的每個元素減 1,求能夠將矩陣的元素全部變成 0。

4、分析與思路

二維前綴和 + 二維差分

5、題解

考慮 M(1, 1) 只能被 (1,1) - (a,b) 的子矩陣處理,所以 (1,1) - (a,b) 的選擇次數是確定的,同理可以求出 (1, 2) - (a, b + 1) 以及其他所有 a x b 的子矩陣的選擇次數。
記 C[i][j] 為 (i, j) - (i + a - 1, j + b - 1) 的選擇次數,可知有:

C[i][j] = M[i][j] - ∑u∑v(C[i - u][j - v])

這個用二維前綴和 + 二維差分就可以在 O(1) 的時間內算出來。最后看是否所有的 C[i][j] 都為非負以及 M 是否恰好會變成全 0 即可。

時間復雜度:O(∑nm)

6、代碼

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define fi first
  4 #define se second
  5 #define all(x) (x).begin(),(x).end()
  6 #define mp make_pair
  7 #define pb push_back
  8 #define input_fast std::ios::sync_with_stdio(false);std::cin.tie(0)
  9 #define local freopen("in.text","r",stdin)
 10 #define pi acos(-1)
 11 const int mo=1e9+7;
 12 typedef long long ll;
 13 typedef pair<int,int> pii;
 14 typedef vector<int> vi;
 15 ll qp(ll a,ll b){if(a==0) return 0;ll res=1;a%=mo;for(;b;b>>=1){if(b&1)res=res*a%mo;a=a*a%mo;}return res;}
 16 template<class T> bool read(T & ret)//ll,int,double,float,+/-
 17 {
 18     char c;int sgn;T bit=0.1;if(c=getchar(),c==EOF) return 0;while(c!='-' && c!='.' && (c<'0' || c>'9')) c=getchar();sgn=(c=='-')?-1:1;ret=(c=='-')?0:(c-'0');while(c=getchar(),c>='0' && c<='9') ret=ret*10+(c-'0');if(c==' '||c=='\n') {ret*=sgn;return 1;}while(c=getchar(),c>='0' && c<='9') ret+=(c-'0')*bit,bit/=10;ret*=sgn;return 1;
 19 }
 20  
 21 int a,b,n,m;
 22 ll g[1010][1010];
 23 ll d[1010][1010];
 24 ll sum[1010][1010];
 25  
 26 void add(int x1,int y1,int x2,int y2,ll c)
 27 {
 28     d[x1][y1]+=c;
 29     d[x2+1][y1]-=c;
 30     d[x1][y2+1]-=c;
 31     d[x2+1][y2+1]+=c;
 32 }
 33  
 34 ll get(int x,int y)
 35 {
 36     return sum[x-1][y]+sum[x][y-1]-sum[x-1][y-1]+d[x][y];
 37 }
 38 void solve()
 39 {
 40     memset(sum,0,sizeof sum);
 41     memset(d,0,sizeof d);
 42     scanf("%d%d%d%d",&n,&m,&a,&b);
 43     for(int i=1;i<=n;++i)
 44     {
 45         for(int j=1;j<=m;j++)
 46         scanf("%lld",&g[i][j]);
 47     }
 48     int ok=1;
 49     for(int i=1;i+a-1<=n;++i)
 50     {
 51         for(int j=1;j+b-1<=m;++j)
 52         {
 53             ll now=g[i][j];
 54             ll pre=get(i,j);
 55              
 56              
 57             sum[i][j]=pre;
 58 /*         
 59                 for(int i=1;i<=n;++i)
 60     {
 61         for(int j=1;j<=m;++j)
 62         {
 63             printf("%d ",sum[i][j]);
 64         }
 65         puts("");
 66     }
 67             */
 68             if(pre==now){
 69                 sum[i][j]=pre;
 70                 continue;
 71             }
 72             else if(pre>now) {
 73                 ok=0;break;
 74             }
 75             else {
 76                 ll tt=now-pre;
 77                 add(i,j,i+a-1,j+b-1,tt);
 78                 sum[i][j] = get(i,j);
 79             }
 80              
 81 //          cout<<"fuck:"<<endl;
 82              
 83      
 84         }
 85          
 86         for(int j=m-b+1;j<=m;++j){
 87             sum[i][j]=get(i,j);
 88             if(sum[i][j]!=g[i][j]) {
 89                 ok=0;break;
 90             }
 91         }
 92         if(!ok) break;
 93     }
 94     for(int i=n-a+1;i<=n;++i)
 95     {
 96      
 97         for(int j=1;j<=m;++j)
 98         {
 99             sum[i][j]=get(i,j);
100             if(sum[i][j]!=g[i][j]) {
101                 ok=0;
102                 break;
103             }
104              
105         }
106         if(!ok) break;
107     }
108 /*  for(int i=1;i<=n;++i)
109     {
110         for(int j=1;j<=m;++j)
111         {
112             printf("%d ",sum[i][j]);
113         }
114         puts("");
115     }
116     */
117     if(!ok) puts("QAQ");
118     else puts("^_^");
119 }
120  
121 signed main(){
122      
123     int t;
124     cin>>t;
125     while(t--)
126     {
127         solve();
128     }
129      
130     return 0;
131 }

(lrj 的代碼)

 

K. Sqrt Approaching

1、題目鏈接

https://ac.nowcoder.com/acm/problem/211824

2、題面

Given three positive integers A, B, n, where n is NOT a perfect square number, you should find two positive integers C, D satisfying (BC - AD)(C - D√n) < 0.
You can assume that solution always exists, and you should print any one of them.

3、題目大意

 

4、分析與思路

5、題解

這個題的本質就是:給定一個分數 X(X = A / B),要求找到另一個分數 Y(Y = C / D),使得 Y 在 X 與 √n 之間。

一個直觀的思路是把 A, B 擴大若干倍,比如就往后面加零直到長度為 99999,得到 C, D,然后再根據 A ^ 2和 n * B ^ 2 的大小關系來給 C, D 進行微調,但這樣是會 WA 的,如果輸入的 A, B, n 滿足 |A ^ 2 - n * B ^ 2| = 1,那么有 |A - √n| = 1 / B * (A + √n * B),這樣的話誤差就是 10 ^ -199980 的級別,然而一般的微調都會產生10^ -10000 的浮動,就算套上二分也很難達到10 ^ -199980 的精度。

於是我們想要一個通解,標程的做法是:

C = (n + 1) * A + 2 * n * B, D = 2 * A + (n+1) * B

可以驗證滿足題目要求的條件。易證從略。

ps.這個題是怎么出出來的?首先本人從某個地方看到了一個題:

是否存在一組正整數n, m,使得 (3 + 5√2) ^ n = (5 + 3√2) ^ m?

這題本人一眼看上去就是不存在,但想了很久不會證,就尋思寫個程序打表找規律,這里不妨把 (a + b√2) ^ n 表示成 A ^ n + B ^ n√2,然后發現 An / Bn 會越來越趨近於 √2,並且似乎對於所有的 a, b 都有這個性質,本人就嘗試證明了一下,進而發現對於其他的根號也有類似的性質,然后就想到出這樣一個根號數值逼近的題了。

6、代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM