CSP-S 2021題解
T1:
首先n^2暴力是直接送的,O(n)列舉劃分方案,O(n)Check即可,對於Check的方法
將進站設定為i,出站設定為-i,維護一個棧即可
考慮上考慮優化時,考慮的是O(n)Check在所難免,於是考慮優化劃分方案數,猜想
函式呈現為單峰函式,然而並不是,只是整體趨勢而已(因此可以採用模擬退火)
正解為利用set進行維護,預處理出國內外分配不同的飛機數所能停靠的數量,字首和
維護,最終O(n)列舉所有方案取max即可,原因在於飛機停靠時刻不同,因此互不影響
時間複雜度O(nlogn),瓶頸在於預處理,類似單調棧每個元素只會進set出set一次。
考場上直接忘記預處理,而採用了錯誤的思路
程式碼如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define LL long long 5 #define C char 6 #define B bool 7 #define V void 8 #define a first 9 #define b second 10 #define P pair<I,I> 11 #define MP make_pair 12 const I N = 1e5 + 3; 13 I n,m1,m2,ans,pre1[N],pre2[N];View Code14 set <P> s,t; 15 inline I read () { 16 I x (0), y (1); C z (getchar ()); 17 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar (); } 18 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar (); 19 return x * y; 20 } 21 signed main () { 22 // freopen ("airport.in","r",stdin);23 // freopen ("airport.out","w",stdout); 24 n = read (), m1 = read (), m2 = read (); 25 for (I i(1);i <= m1; ++ i) { 26 I x (read ()), y (read ()); 27 s.insert (MP (x,y)); 28 } 29 for (I i(1);i <= m2; ++ i) { 30 I x (read ()), y (read ()); 31 t.insert (MP (x,y)); 32 } 33 for (I i(1);i <= n; ++ i) { 34 I sum (0); 35 set <P> :: iterator p1 (s.begin ()),p2; 36 while (p1 != s.end ()) { 37 p2 = s.lower_bound (MP ((*p1).b,(*p1).a)); 38 s.erase (p1), p1 = p2, sum ++ ; 39 } 40 pre1[i] = pre1[i - 1] + sum; 41 } 42 for (I i(1);i <= n; ++ i) { 43 I sum (0); 44 set <P> :: iterator p1 (t.begin ()),p2; 45 while (p1 != t.end ()) { 46 p2 = t.lower_bound (MP ((*p1).b,(*p1).a)); 47 t.erase (p1), p1 = p2, sum ++ ; 48 } 49 pre2[i] = pre2[i - 1] + sum; 50 } 51 for (I i(0);i <= n; ++ i) 52 ans = max (ans,pre1[i] + pre2[n - i]); 53 printf ("%d\n",ans); 54 return 0; 55 }
T2:
暴力列舉字串+O(n^2)Check有15pts,然而因為Check太麻煩而沒打。
正解為區間dp,考場上完全沒有想到dp,以為是數學題,做題的套路並不太對
考慮設f[l][r]表示l~r區間合法字串數量,轉移列舉分界點+乘法原理即可,然而
會算重,考慮增加限制進行狀態劃分,設f[l][r]表示l~r中左右端點括號匹配的方案數
g[l][r]表示l~r左右端點括號不匹配的方案數,轉移針對不同字串進行
O(n^2)預處理出p[l][r]表示l~r是否能完全表示成S形式,則
(S):f[l][r] += p[l][r]
(A):f[l][r] += f[l + 1][r - 1] + g[l + 1][r - 1]
(SA):f[l][r] += sigma(i,1,k)f[l + i + 1][r - 1] + g[l + i + 1][r - 1]
(AS):f[l][r] += sigma(i,1,k)f[l + 1][r - i - 1] + g[l + 1][r - i - 1]
ASB||AB:g[l][r] += (f[l][i] + g[l][i]) * f[j][r] (l < i < j < r)
(其實就是列舉某一段全部為*,因此需要判斷該段是否能全部為*) 最後一個轉移是欽定後半部分為f,避免算重,手模即可(需要學習這種避免算重的方法)以後模擬賽一定要打暴力
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I long long 4 #define C char 5 #define B bool 6 #define V void 7 #define LL long long 8 #define memset(name,val,typ,len) memset (name,val,sizeof (typ) * len) 9 const I N = 505, mod = 1e9 + 7; 10 C s[N]; 11 B p[N][N]; 12 I n,k,f[N][N],g[N][N],nxt[N]; 13 inline I read () { 14 I x (0), y (1); C z (getchar ()); 15 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar (); } 16 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar (); 17 return x * y; 18 } 19 inline V Mod1 (I &a,const I &b) { 20 a = a + b > mod ? a + b - mod : a + b; 21 } 22 signed main () { 23 n = read (), k = read (), scanf ("%s",s + 1); 24 for (I i(1);i <= n; ++ i) { 25 B flag (0); 26 for (I j(i);j <= n; ++ j) 27 (s[j] == '*' || s[j] == '?') && !flag ? p[i][j] = 1 : flag = 1; 28 f[i][i + 1]= (s[i] == '(' || s[i] == '?') && (s[i + 1] == ')' || s[i + 1] == '?'); 29 } 30 for (I len(3);len <= n; ++ len) { 31 for (I l(1),r(len);r <= n; ++ l, r = l + len - 1) { 32 if ((s[l] != '(' && s[l] != '?') || (s[r] != ')' && s[r] != '?')) continue; 33 if (len <= k + 2) f[l][r] += p[l + 1][r - 1]; 34 Mod1 (f[l][r],(f[l + 1][r - 1] + g[l + 1][r - 1]) % mod); 35 for (I i(1);i <= min (k,r - l - 2); ++ i) { 36 if (p[l + 1][l + i]) 37 Mod1 (f[l][r],(f[l + i + 1][r - 1] + g[l + i + 1][r - 1]) % mod); 38 if (p[r - i][r - 1]) 39 Mod1 (f[l][r],(f[l + 1][r - i - 1] + g[l + 1][r - i - 1]) % mod); 40 } 41 I pos (0), pre (0); 42 for (I i(l + 1);i <= r - 2; ++ i) { 43 pos = max (pos,i + 1); 44 while ((s[pos] == '*' || s[pos] == '?') && pos <= r - 2) pos ++ ; 45 nxt[i] = min (i + k + 1,pos); 46 } 47 for (I i(l + 2);i <= nxt[l + 1]; ++ i) Mod1 (pre,f[i][r]); 48 Mod1 (g[l][r],(f[l][l + 1] + g[l][l + 1]) * pre % mod); 49 for (I i(l + 2);i <= r - 2; ++ i) { 50 pre = (pre - f[i][r] + mod) % mod; 51 for (I j(nxt[i - 1] + 1);j <= nxt[i]; ++ j) 52 Mod1 (pre,f[j][r]); 53 Mod1 (g[l][r],(f[l][i] + g[l][i]) * pre % mod); 54 } 55 } 56 } 57 printf ("%lld\n",(f[1][n] + g[1][n])% mod); 58 }View Code
T3:
考慮首先暴力比較好想,Dfs遍歷即可,考慮剪枝,首先根據要求
在尋找前n個位置時顯然不能出現重複的數,因此利用陣列記錄當前出現過的數即可
然後,因為要構成迴文,在遍歷後n個數時,可以通過判斷新增該數是否能形成迴文進行剪枝
然而還是有很多冗餘狀態,考慮既然要形成迴文,那麼顯然可以雙向進行,因此維護4個指標
分別代表前回文拓展到的位置,後迴文拓展到的位置,雙向搜尋。
觀察問題發現要求字典序最小,那麼可以進行最優性剪枝,先遍歷l,後遍歷r,得到結果直接
輸出並return即可。
綜合上述剪枝已經可以通過民間資料,考慮繼續優化,發現在Dfs過程中顯然4指標會遍歷到重複位置
也就是可以記憶化,利用unordered_map記錄已經拓展過的位置(結構體中存放4指標),實際上這已經是
正解,然而由於使用map,因此複雜度為O(nlogn)
考慮上述過程顯然並不需要Dfs實現,拓展出來,直接先左後右4指標進行拓展,若向左拓展不合法
那麼向右拓展一定不合法(根據Dfs很容易理解,每次指標移動僅為1),因此直接遍歷即可,時間複雜度O(n)
程式碼如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define C char 5 #define B bool 6 #define V void 7 #define LL long long 8 #define memset(name,val,typ,len) memset (name,val,sizeof (typ) * len) 9 const I N = 1e6 + 3; 10 I T,n,a[N]; 11 B flag; 12 C s[N]; 13 inline I read () { 14 I x (0), y (1); C z (getchar ()); 15 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar (); } 16 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar (); 17 return x * y; 18 } 19 V Dfs (I x,I l1,I r1,I l2,I r2) { 20 if (x == n >> 1) { 21 puts (s), flag = 1; 22 return ; 23 } 24 if (a[l1] == a[l2] && l1 < l2) { 25 s[x] = s[n - x - 1] = 'L'; 26 Dfs (x + 1,l1 + 1,r1,l2 - 1,r2); 27 } 28 if (flag) return ; 29 if (a[l1] == a[r2]) { 30 s[x] = 'L', s[n - x - 1] = 'R'; 31 Dfs (x + 1,l1 + 1,r1,l2,r2 + 1); 32 } 33 if (flag) return ; 34 if (a[r1] == a[l2]) { 35 s[x] = 'R', s[n - x - 1] = 'L'; 36 Dfs (x + 1,l1,r1 - 1,l2 - 1,r2); 37 } 38 if (flag) return ; 39 if (a[r1] == a[r2] && r2 < r1) { 40 s[x] = s[n - x - 1] = 'R'; 41 Dfs (x + 1,l1,r1 - 1,l2,r2 + 1); 42 } 43 } 44 signed main () { 45 // freopen ("palin.in","r",stdin); 46 // freopen ("palin.out","w",stdout); 47 T = read (); 48 while (T -- ) { 49 I pos1,pos2; 50 n = read () << 1; flag = 0; s[n] = 0; 51 for (I i(1);i <= n; ++ i) 52 a[i] = read (); 53 for (I i(1);i <= n; ++ i) { 54 if (a[i] == a[1] && i != 1) pos1 = i; 55 if (a[i] == a[n] && i != n) pos2 = i; 56 } 57 s[n - 1] = 'L'; 58 s[0] = 'L', Dfs (1,2, n ,pos1 - 1,pos1 + 1); 59 if ( flag) continue ; 60 s[0] = 'R', Dfs (1,1,n - 1,pos2 - 1,pos2 + 1); 61 if (!flag) puts ("-1"); 62 } 63 }View Code
(純Dfs,很容易被卡)
T4:
首先一個結論就是最終的局面一定是存在一條分割線使得線兩側顏色不同,且同側顏色完全相同,
證明考慮反證,若同側顏色不同那麼顯然存在更優的方案
於是問題轉化為求最小割,網路流Dinic即可。
對於此類兩點之間連邊無方向性,網路流建圖時殘量網路權值也設定為z即可
注意慎用memcpy,容易出鍋
程式碼如下:1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I long long 4 #define C char 5 #define B bool 6 #define V void 7 #define LL long long 8 #define memset(name,val,typ,len) memset (name,val,sizeof (typ) * len) 9 const I N = 505; 10 I s,t,d[N*N],now[N*N],maxflow; 11 I T,n,m,cnt,idx[N][N],rec1[N*N],rec2[N*N << 3],size,tmp; 12 I tot,head[N*N],to[N*N << 3],nxt[N*N << 3],wgt[N*N << 3]; 13 inline I read () { 14 I x (0), y (1); C z (getchar ()); 15 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar (); } 16 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar (); 17 return x * y; 18 } 19 inline V found (I x,I y,I z) { 20 to[++tot] = y, nxt[tot] = head[x], wgt[tot] = z, head[x] = tot; 21 to[++tot] = x, nxt[tot] = head[y], wgt[tot] = z, head[y] = tot; 22 } 23 inline V build (I x,I y,I z) { 24 to[++tot] = y, nxt[tot] = head[x], wgt[tot] = z, head[x] = tot; 25 to[++tot] = x, nxt[tot] = head[y], wgt[tot] = 0, head[y] = tot; 26 } 27 B Bfs () { 28 memset (d,0,I,size); 29 for (I i(1);i <= size; ++ i) 30 now[i] = head[i]; 31 queue <I> q; 32 q.push (s), d[s] = 1; 33 while (!q.empty ()) { 34 I x (q.front ()); q.pop (); 35 for (I i(now[x]); i ;i = nxt[i]) 36 if (wgt[i] && !d[to[i]]) { 37 d[to[i]] = d[x] + 1; 38 if (to[i] == t) return true; 39 q.push (to[i]); 40 } 41 } 42 return false; 43 } 44 I Dfs (I x,I flow) { 45 if (x == t) return flow; 46 I rest (flow), tmp; 47 for (I i(now[x]); i ;i = nxt[i]) 48 if (wgt[i] && d[to[i]] == d[x] + 1) { 49 tmp = Dfs (to[i],min (wgt[i],rest)); 50 if (!tmp) d[to[i]] = 0; 51 wgt[i] -= tmp, wgt[i ^ 1] += tmp, rest -= tmp; 52 if (!rest) { now[x] = i; break; } 53 } 54 return flow - rest; 55 } 56 inline I getid (I id) { 57 if (id <= m) return idx[1][id]; 58 if (id <= n + m) return idx[id - m][m]; 59 if (id <= n + m * 2) return idx[n][n + m * 2 + 1 - id]; 60 if (id <= n * 2 + m * 2) return idx[n * 2 + m * 2 + 1 - id][1]; 61 } 62 signed main () { 63 n = read (), m = read (), T = read (), tot = 1; 64 s = ++ cnt; 65 for (I i(1);i <= n; ++ i) 66 for (I j(1);j <= m; ++ j) 67 idx[i][j] = ++ cnt; 68 t = ++ cnt; 69 for (I i(1);i < n; ++ i) 70 for (I j(1);j <= m; ++ j) 71 found (idx[i][j],idx[i + 1][j],read ()); 72 for (I i(1);i <= n; ++ i) 73 for (I j(1);j < m; ++ j) 74 found (idx[i][j],idx[i][j + 1],read ()); 75 size = cnt + n * 2 + m * 2 + 1, tmp = tot; 76 for (I i(1);i <= size; ++ i) rec1[i] = head[i]; 77 for (I i(1);i <= tot; ++ i) rec2[i] = wgt[i]; 78 while (T -- ) { 79 I k (read ()); maxflow = 0; 80 for (I i(1);i <= size; ++ i) head[i] = rec1[i]; 81 for (I i(1);i <= tot; ++ i) wgt[i] = rec2[i]; 82 tot = tmp; 83 for (I i(1);i <= k; ++ i) { 84 I w (read ()), id (read ()); found (cnt + id,getid (id),w); 85 read () ? build (s,cnt + id,INT_MAX) : build (cnt + id,t,INT_MAX); 86 } 87 while (Bfs ()) maxflow += Dfs (s,INT_MAX); 88 printf ("%lld\n",maxflow); 89 } 90 }View Code