1. 程式人生 > >比賽-暑假訓練賽1 (26 Jul, 2018)

比賽-暑假訓練賽1 (26 Jul, 2018)

答案 ace 第一個 emp 通過 for 問題 暑假 移動

A. 密碼 沒有想到題解裏機智的 P2-P1 避免重復計算,我以為是一個類似最長公共子串的 DP,OrzOrzOrz。 設輸入分別為 A, B 兩個字符串。f[i][j] 表示 A 串前 i 位與 B 串前 j 位匹配( B 串中字母可以依次在 A 中找到)的方案數。 當 A[i] == B[j], f[i][j] = f[i-1][j-1] 當 A[i] != B[j], f[i][j] = f[i-1][j] 。 顯然 f[i][0] = i 。然後直接這樣寫會爆空間,註意到 i 這維可以滾動掉。答案是 ∑f[i][lenB] 。

B. 獨立集 如果 i < j 且 A[i] < A[j] ,那麽 i, j 就不會連邊,就能夠放在一個集合裏。再推一下會發現第一問是求最長上升子序列。第二問可以腦補出這個東西:某個點確定的充要條件是:原序列以它為最後一個元素和第一個元素的單升,都有且僅有 1 個。後者可以轉換為:反過來的序列裏以它為最後一個元素的單降有且僅有 1 個。把 for 反著寫然後讓A[i] = -A[i],就把反向單降轉化得和正向單升一樣了。

C. 益智遊戲 先跑最短路,然後枚舉邊 x->y = w,對 dis(A, x) + w + dis(y, B) = dis(A, B) 且 dis(C, x) + w + dis(y, D) = dis(C, D) 的邊做一個標記。顯然最終的點都在這些邊上。然後問題轉化為求最長鏈+1(點數)。可以用 DP,f[x] = max{ f[i] } + 1 (滿足有邊從 i 到 x) 。其實寫起來是記憶化搜索。由於上次做過一道什麽轟炸城市的題,也是求最長鏈,被大佬們告知直接 topsort 就可以了不用搜索,所以考試的時候寫了發 topsort 。另外樣例第 2 組就是菊花圖,會卡掉 SPFA (因為邊數有 200000,非常大)。

技術分享圖片
 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 long long f[250];
 5 char A[320000], B[250];
 6 
 7 int main()
 8 {
 9     long long ans = 0;
10     int Alen, Blen, i, j;
11     scanf("%s%s", A+1, B+1);
12     Alen = strlen(A+1), Blen = strlen(B+1);
13     for (i = 1; i <= Alen; ++i) {
14 f[0] = i; 15 for (j = Blen; j >= 1; --j) 16 if (A[i] == B[j]) f[j] = f[j-1]; 17 ans += f[Blen]; 18 } 19 printf("%lld\n", ans); 20 return 0; 21 }
A 技術分享圖片
 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 
 5 using namespace std;
 6 
 7 const int _N = 120000;
 8 const int INF = 1e9;
 9 
10 int A[_N], f[_N], X[_N], Y[_N], cnt[_N];
11 
12 int main()
13 {
14     int i, N, len;
15     scanf("%d", &N);
16     for (i = 1; i <= N; ++i)
17         scanf("%d", &A[i]);
18     f[len = 0] = -INF;
19     for (i = 1; i <= N; ++i) {
20         if (A[i] > f[len]) { f[++len] = A[i], X[i] = len; continue; }
21         int p = lower_bound(f+1, f+1+len, A[i])-f;
22         f[p] = A[i], X[i] = p;
23     }
24     printf("%d\n", len);
25     f[len = 0] = -INF;
26     for (i = N; i >= 1; --i) {
27         if (-A[i] > f[len]) { f[++len] = -A[i], Y[i] = len; continue; }
28         int p = lower_bound(f+1, f+1+len, -A[i])-f;
29         f[p] = -A[i], Y[i] = p;
30     }
31     for (i = 1; i <= N; ++i)
32         if (X[i]+Y[i]-1 == len) ++cnt[X[i]];
33     for (i = 1; i <= N; ++i)
34         if (X[i]+Y[i]-1 == len && cnt[X[i]] == 1) printf("%d ", i);
35     return 0;
36 }
B 技術分享圖片
 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <vector>
 4 #include <queue>
 5 
 6 using namespace std;
 7 
 8 typedef long long ll;
 9 
10 const int _N = 55005;
11 const ll INF = 1e15;
12 
13 struct edge {
14     int v; ll w;
15     edge(int v = 0, ll w = 0):
16         v(v), w(w) { }
17     bool operator < (edge const &tmp) const
18     {
19         return w > tmp.w;
20     }
21 };
22 
23 vector<edge> G[3][_N];
24 priority_queue<edge> Q;
25 queue<edge> Q2;
26 int N, M, beg1, end1, beg2, end2, ind[_N];
27 ll dis1[_N], dis2[_N], anti1[_N], anti2[_N];
28 bool mk[_N];
29 
30 void Ins(int id, int x, int y, ll w) { G[id][x].push_back(edge(y, w)); return; }
31 
32 void Dijkstra(int id, int beg, ll *dis)
33 {
34     int i;
35     for (i = 1; i <= N; ++i) dis[i] = INF;
36     dis[beg] = 0;
37     while (!Q.empty()) Q.pop();
38     Q.push(edge(beg, dis[beg]));
39     while (!Q.empty()) {
40         int p = Q.top().v;
41         vector<edge>::iterator it;
42         if (Q.top().w != dis[p]) { Q.pop(); continue; }
43         for (it = G[id][p].begin(); it != G[id][p].end(); ++it) {
44             if (dis[it->v] <= dis[p] + it->w) continue;
45             dis[it->v] = dis[p] + it->w;
46             Q.push(edge(it->v, dis[it->v]));
47         }
48         Q.pop();
49     }
50     return ;
51 
52 }
53 
54 int main()
55 {
56     int i, j;
57     scanf("%d%d", &N, &M);
58     for (i = 1; i <= M; ++i) {
59         int x, y; ll w;
60         scanf("%d%d%lld", &x, &y, &w);
61         Ins(0, x, y, w), Ins(1, y, x, w);
62     }
63     scanf("%d%d%d%d", &beg1, &end1, &beg2, &end2);
64     Dijkstra(0, beg1, dis1), Dijkstra(0, beg2, dis2);
65     Dijkstra(1, end1, anti1), Dijkstra(1, end2, anti2);
66     if (dis1[end1] >= INF || dis2[end2] >= INF) { printf("-1\n"); return 0; }
67     for (i = 1; i <= N; ++i)
68         for (j = G[0][i].size()-1; j >= 0; --j) {
69             edge p = G[0][i][j];
70             if (dis1[i] >= INF || dis2[i] >= INF || anti1[p.v] >= INF || anti2[p.v] >= INF) continue;
71             if (dis1[i]+p.w+anti1[p.v] == dis1[end1] && dis2[i]+p.w+anti2[p.v] == dis2[end2])
72                 Ins(2, i, p.v, p.w), ++ind[p.v];
73         }
74     for (i = 1; i <= N; ++i)
75         if (!ind[i])
76             Q2.push(edge(i, 1));//-------------------------
77     ll ans = 0;
78     while (!Q2.empty()) {
79         edge p = Q2.front(); Q2.pop();
80         ans = max(ans, p.w);
81         for (int i = G[2][p.v].size()-1; i >= 0; --i) {
82             int t = G[2][p.v][i].v;
83             if (!--ind[t]) Q2.push(edge(t, p.w+1));
84         }
85     }
86     printf("%lld\n", ans);
87     return 0;
88 }
C

A密碼
時間限制 : 10000 MS 空間限制 : 165536 KB
評測說明 : 1s
問題描述

假發通過了不懈的努力,得到了將軍家門鎖的密碼(一串小寫英文字母)。但是假發被 十四和猩猩他們盯上了,所以假發需要把密碼傳遞出去。因為假發不想十四他們發現幾松門 前貼的小紙條就是將軍家的密碼,所以他加密了密碼(新八:聽起來有點詭異)。加密方法 如下:隨機地,在密碼中任意位置插入隨機長度的小寫字符串。 不過,假發相信銀桑和他那麽多年小學同學,一定能猜中密碼是什麽的(新八:銀桑什 麽時候成攮夷誌士了!!!)。可是,寫完了小紙條之後,假發覺得有點長,就想截去頭和 尾各一段(可以為空),讓剩下的中間那一段依然包含真~密碼。想著想著,假發就想知道 有多少種可行方案。結果在沈迷於稿紙之際,假發被投進了獄門島(新八:……)。於是, 就由你計算了。

輸入格式

兩行非空字符串,純小寫英文字母,第一行是加密後的密碼,第二行是原密碼。
第一行長度不超過 300000,第二行不超過 200。

輸出格式

一行,有多少種方案。註意:不剪也是一種方案。

樣例輸入 1


abcabcabc
cba

樣例輸出 1

9

樣例輸入 2

abcabcabac
cba

樣例輸出 2

18

提示

【樣例1解釋】 用(L,R)表示一種方案,其中L和R分別表示截去頭和尾的長度。這9鐘方案分別是 (0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)。

B獨立集
時間限制 : 20000 MS 空間限制 : 165536 KB
評測說明 : 1s
問題描述

技術分享圖片

輸入格式

輸入包含兩行,第一行為 N,
第二行為 1 到 N 的一個全排列

輸出格式

輸出包含兩行,第一行輸出最大獨立集的大小,第二行從小到大輸出一定在最大獨立集 的點的編號。

樣例輸入 1

樣例輸出 1

樣例輸入 2

15
4 14 9 12 11 1 5 6 2 7 13 15 8 10 3

樣例輸出 2

6
7 8 10

樣例輸入 3

3
3 1 2

樣例輸出 3

2
2 3

提示

30%的數據滿足 N<=16
60%的數據滿足 N<=1,000
100%的數據滿足 N<=100,000

C益智遊戲
時間限制 : 10000 MS 空間限制 : 165536 KB
評測說明 : 1s
問題描述

小 P 和小 R 在玩一款益智遊戲。遊戲在一個正權有向圖上進行。 小 P 控制的角色要從 A 點走最短路到 B 點,小 R 控制的角色要從 C 點走最短路到 D 點。 一個玩家每回合可以有兩種選擇,移動到一個相鄰節點或者休息一回合。 假如在某一時刻,小 P 和小 R 在相同的節點上,那麽可以得到一次特殊獎勵,但是在每 個節點上最多只能得到一次。 求最多能獲得多少次特殊獎勵。

輸入格式

第一行兩個整數 n,m 表示有向圖的點數和邊數。 接下來 m 行每行三個整數 xi,yi,li,表示從 xi到 yi有一條長度為 li的邊。 最後一行四個整數 A,B,C,D,描述小 P 的起終點,小 R 的起終點。

輸出格式

輸出一個整數表示最多能獲得多少次特殊獎勵。若小 P 不能到達 B 點或者小 R 不能到達 D 點則輸出-1。

樣例輸入

5 5
1 2 1
2 3 2
3 4 4
5 2 3
5 3 5
1 3 5 4

樣例輸出


2

提示

【數據規模】
對於 30%的數據,滿足 n≤50
對於 60%的數據,滿足 n≤1000,m≤5000 對於
100%的數據,滿足 n≤50000,m≤200000,1≤li≤500000000

比賽-暑假訓練賽1 (26 Jul, 2018)