比赛-暑假训练赛1 (26 Jul, 2018)
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 }
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 }
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 }
A密码 | ||
|
问题描述
假发通过了不懈的努力,得到了将军家门锁的密码(一串小写英文字母)。但是假发被 十四和猩猩他们盯上了,所以假发需要把密码传递出去。因为假发不想十四他们发现几松门 前贴的小纸条就是将军家的密码,所以他加密了密码(新八:听起来有点诡异)。加密方法 如下:随机地,在密码中任意位置插入随机长度的小写字符串。 不过,假发相信银桑和他那么多年小学同学,一定能猜中密码是什么的(新八:银桑什 么时候成攮夷志士了!!!)。可是,写完了小纸条之后,假发觉得有点长,就想截去头和 尾各一段(可以为空),让剩下的中间那一段依然包含真~密码。想着想着,假发就想知道 有多少种可行方案。结果在沉迷于稿纸之际,假发被投进了狱门岛(新八:……)。于是, 就由你计算了。
输入格式
两行非空字符串,纯小写英文字母,第一行是加密后的密码,第二行是原密码。
第一行长度不超过 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独立集 | ||
|
问题描述
输入格式
输入包含两行,第一行为 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益智游戏 | ||
|
问题描述
小 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