比赛-暑假训练赛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 }
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 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

posted @ 2018-07-26 23:37  derchg  阅读(263)  评论(0编辑  收藏  举报