【首先检讨,昨天晚上误解以为今天没有比赛,所以到实验室已经1个小时过去了,就lh大仙一个单刷,Orz】
【然后是来的路上惹怒了niuniu,我的错】
本场比赛是11年hnu出的多校训练,往往多校训练的题目质量都是比较好的,水题不会太多,考的知识面比较广和深或者很偏很难。
由于到实验室迟到了1个小时,lh大仙挂了B,H题和I都被秒了,于是我看了H,过了之后发现F题被dongfang队秒掉,我又转看F,由此陷入了深深的泥潭。Orz,膜拜F题1A!之后就去401一起做题了。
【H题】给定数N<=10^9,求a^2+b^2 = N的序对数,注意a,b可以正负。显然sqrt(N)暴力即可。59min, 1A。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 5 long long sq[1001000]; 6 int cnt; 7 int judge(int m) 8 { 9 int l = 1, r = cnt; 10 while (l <= r) 11 { 12 int mid = (l + r)/2; 13 if (sq[mid] == m) 14 { 15 return true; 16 } 17 if (sq[mid] < m){ 18 l = mid + 1; 19 }else{ 20 r = mid - 1; 21 } 22 } 23 return false; 24 } 25 int calc(long long n) 26 { 27 if (n == 0) 28 return 1; 29 int res = 0; 30 for (int i=1;i*i<=n;i++) 31 { 32 if (i*i == n) 33 { 34 res += 4; 35 }else{ 36 if (judge(n - i * i)) 37 { 38 if (i*i == n - i*i) 39 res += 4; 40 else 41 res += 4; 42 } 43 } 44 } 45 46 return res; 47 } 48 int main() 49 { 50 cnt = 0; 51 for (long long i=1;i*i<=1000000000;i++) 52 { 53 sq[++cnt] = i * i; 54 } 55 long long n; 56 while (scanf("%I64d",&n)==1) 57 { 58 printf("%d\n",calc(n)); 59 } 60 return 0; 61 }
【此时I题】已经被大仙秒掉,后面知道这个题是白书上的原题,他直接抄的代码。
I题,给定N个点,M条边的有向图,问至少加入多少条边,使得整个图为强连通的。scc+统计入度和出度为0的点,然后YY。模板题。61min,1A。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <string.h> 6 #include <string> 7 #include <vector> 8 #include <stack> 9 10 using namespace std; 11 12 const int MAXN = 100000 + 10; 13 vector<int> G[MAXN]; 14 int T, n, m; 15 int pre[MAXN], lowlink[MAXN], sccno[MAXN], dfs_clock, scc_cnt; 16 int in0[MAXN], out0[MAXN]; 17 stack <int> S; 18 19 void dfs(int u) 20 { 21 pre[u] = lowlink[u] = ++dfs_clock; 22 S.push(u); 23 for (int i = 0; i < G[u].size(); i++) 24 { 25 int v = G[u][i]; 26 if (!pre[v]) 27 { 28 dfs(v); 29 lowlink[u] = min(lowlink[u], lowlink[v]); 30 } 31 else if (!sccno[v]) 32 { 33 lowlink[u] = min(lowlink[u], pre[v]); 34 } 35 } 36 37 if (lowlink[u] == pre[u]) 38 { 39 scc_cnt++; 40 for (;;) 41 { 42 int x = S.top(); S.pop(); 43 sccno[x] = scc_cnt; 44 if (x == u) break; 45 } 46 } 47 } 48 49 void find_scc(int n) 50 { 51 dfs_clock = scc_cnt = 0; 52 memset(sccno, 0, sizeof(sccno)); 53 memset(pre, 0, sizeof(pre)); 54 for (int i = 0; i < n; i++) 55 { 56 if (!pre[i])dfs(i); 57 } 58 } 59 60 void input() 61 { 62 while (scanf("%d %d", &n, &m) != EOF) 63 { 64 for (int i = 0; i < n; i++) 65 { 66 G[i].clear(); 67 } 68 for (int i = 0; i < m; i++) 69 { 70 int u, v; 71 scanf("%d %d", &u, &v); u--; v--; 72 G[u].push_back(v); 73 } 74 75 find_scc(n); 76 77 for (int i = 1; i <= scc_cnt; i++) in0[i] = out0[i] = 1; 78 for (int u = 0; u < n; u++) 79 { 80 for (int i = 0; i < G[u].size(); i++) 81 { 82 int v = G[u][i]; 83 if (sccno[u] != sccno[v]) in0[sccno[v]] = out0[sccno[u]] = 0; 84 } 85 } 86 87 int a = 0, b = 0; 88 for (int i = 1; i <= scc_cnt; i++) 89 { 90 if (in0[i]) a++; 91 if (out0[i]) b++; 92 } 93 94 int ans = max(a, b); 95 if (scc_cnt == 1) ans = 0; 96 printf("%d\n", ans); 97 } 98 } 99 100 int main() 101 { 102 input(); 103 return 0; 104 }
F题:给定N和1到N的一个排列,问序列中存不存在3个元素的等差子序列,Pi1 - Pi2 = Pi2 - Pi3,i1<i2<i3。N《=10000,时限4s。
我和niuniu讨论了下F题,尝试了一下暴力枚举N^2,g++提交TLE,然后发现有60组case之多。
我查看了一下dongfang队的记录,他们代码长度816 B(我们的743 B),内存使用384 KB,也就是差不多20000W的int型,运行了3187ms。
我认定了他们是暴力水过的。
Orz,niuniu给出了一种枚举的优化方式,枚举公差d,缩小范围,总复杂度控制在10^9内。我balabala写完提交,TLE。此时,我们又陷入了深深的泥潭。
lh大仙的B题,大意是,有N个cat和M个dog,有P个人,每个人喜欢某个cat(dog)和讨厌某个dog(cat),如果去掉了他不喜欢的动物保留了喜欢的,那么他就会开心。问,去掉一些动物最多能使得多少人开心。
这出现在白书上最大匹配的练习题目,没有给出解法。lh大仙就咬住一定是最大匹配。
这时,niuniu在继续做F,也在读别的题目,我想了想B,也读了读其他几个题目。
B题,我们始终以cat和dog为对象建立二分图求最大匹配,陷入了深深的泥潭,不得正解。144分钟的时候,dongfang队过了B题,我们分明听到了他们的讨论,确实是和匹配有关。
Orz,他们此时4题秒杀我们2题。
我看了board,B\E\F是此时过得最多的题目。
【听niuniu说了E题】,大意是,给定N个圆,求去掉最多多少个圆,使得第1、2、3个圆还能连通,3<=N<=200。
我推了推E,建图之后无法建立费用流模型,也找不出平面上圆相交的特殊性质。
后来灵光一闪,最终的路径,肯定是Y字形,也就是只用枚举A、B、C的第一个交点D,然后分别D到A\B\C的最短路。
写了个Floyd,提交TLE。case数比较多?转用了dijkstra,178min FB。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<algorithm> 5 6 int n; 7 struct cir{ 8 int x, y, r; 9 }sq[222]; 10 int mp[222][222]; 11 int calc(int p, int q) 12 { 13 return ((sq[p].x - sq[q].x) * (sq[p].x - sq[q].x) + (sq[p].y - sq[q].y) * (sq[p].y - sq[q].y) <= (sq[p].r + sq[q].r)*(sq[p].r + sq[q].r)); 14 } 15 int dis[4][222]; 16 int vis[222]; 17 void spfa(int S, int * f) 18 { 19 memset(vis, 0, sizeof(vis)); 20 for (int i=1;i<=n;i++) 21 f[i] = 100000; 22 f[S] = 1; 23 for (int i=1;i<=n;i++) 24 { 25 int p = 1; 26 int mm = 100000; 27 for (int j=1;j<=n;j++) 28 if (!vis[j] && f[j]<mm){ 29 mm = f[j]; 30 p = j; 31 } 32 vis[p] = 1; 33 for (int j=1;j<=n;j++) 34 if(!vis[j] && mp[p][j] && f[p] + mp[p][j] < f[j]) 35 { 36 f[j] = f[p] + 1; 37 } 38 } 39 return ; 40 } 41 int solve() 42 { 43 int A = 1, B = 2, C = 3; 44 spfa(A,dis[A]); 45 spfa(B,dis[B]); 46 spfa(C,dis[C]); 47 48 int res = -1; 49 for (int d = 1;d<=n;d++) 50 { 51 if (dis[A][d]< 100000&& dis[B][d]!=100000 && dis[C][d]!=100000) 52 { 53 int tmp = dis[A][d] + dis[B][d] + dis[C][d] - 2; 54 if (res == -1 || tmp < res) 55 { 56 res = tmp; 57 } 58 } 59 } 60 return res; 61 } 62 int main() 63 { 64 int cas; 65 scanf("%d",&cas); 66 while (cas--) 67 { 68 scanf("%d", &n); 69 for (int i=1;i<=n;i++) 70 { 71 scanf("%d%d%d", &sq[i].x, &sq[i].y, &sq[i].r); 72 } 73 for (int i=1;i<=n;i++) 74 { 75 for (int j=1;j<=n;j++) 76 { 77 mp[i][j] = calc(i,j); 78 } 79 } 80 int ans = solve(); 81 if (ans == -1) 82 printf("%d\n",ans); 83 else{ 84 printf("%d\n", n - ans); 85 } 86 } 87 return 0; 88 }
期间我读了D题,和niuniu讨论,此题是lcs的变形,我认为是可做的,让niuniu分析分析。
【然后我又继续YY F题】,我换用了C语法,使用C提交,居然AC了!时间2671ms,然后我把之前那个使用c++提交,也过了2671ms。235min,3A。
跪:G++、C++、GCC,在运行上确实差异很多,C++运行要快,G++读写快。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <string.h> 3 4 #define maxn 10005 5 6 int seq[maxn]; 7 int hash[maxn]; 8 int n; 9 bool calc() 10 { 11 int d, i,tmp = n; 12 for (d = 1;d<=n/2;d++) 13 { 14 tmp -= 2; 15 for (i=1;i<=tmp;i++) 16 { 17 if (hash[i] < hash[i+d] && hash[i+d] < hash[i+ d+ d]) 18 { 19 return true; 20 } 21 if (hash[i] > hash[i+d] && hash[i+d] > hash[i+ d+ d]) 22 { 23 return true; 24 } 25 26 } 27 } 28 return false; 29 } 30 int main() 31 { 32 int T,i,x; 33 scanf("%d", &T); 34 while(T--) 35 { 36 scanf("%d", &n); 37 for(i = 1; i <= n; i++) 38 { 39 scanf("%d", &x); 40 hash[x] = i; 41 } 42 if (calc()) 43 puts("Y"); 44 else 45 puts("N"); 46 } 47 return 0; 48 }
【B】然后lh大仙有想法要写B题,就交电脑给他coding。
居然不知不觉把B给过了,243min A掉。太神奇了有没有!!原来是对人进行建图。是个好题啊。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <string.h> 6 #include <string> 7 8 using namespace std; 9 10 const int MAXN = 500 + 10; 11 int n, m, k; 12 int c[MAXN][MAXN]; 13 bool vis[MAXN]; 14 int match[MAXN]; 15 16 bool dfs(int u) 17 { 18 for (int i = 0; i < k; i++) 19 { 20 if (!vis[i] && c[u][i]) 21 { 22 vis[i] = true; 23 if (match[i] == -1 || dfs(match[i])) 24 { 25 match[i] = u; 26 return true; 27 } 28 } 29 } 30 31 return false; 32 } 33 34 void solve() 35 { 36 int res = 0; 37 38 for (int i = 0; i < k; i++) 39 { 40 memset(vis, false, sizeof(vis)); 41 if (dfs(i)) 42 { 43 res++; 44 } 45 } 46 printf("%d\n", k - res / 2); 47 } 48 49 struct Node 50 { 51 string a, b; 52 }node[MAXN]; 53 54 void input() 55 { 56 while (scanf("%d %d %d", &n, &m, &k) != EOF) 57 { 58 int a, b; 59 memset(c, 0, sizeof(c)); 60 memset(match, -1, sizeof(match)); 61 for (int i = 0; i < k; i++) 62 { 63 cin >> node[i].a >> node[i].b; 64 } 65 66 for (int i = 0; i < k; i++) 67 { 68 for (int j = 0; j < k; j++) 69 { 70 if (node[i].a == node[j].b) 71 { 72 c[i][j] = c[j][i] = 1; 73 } 74 } 75 } 76 77 solve(); 78 } 79 } 80 81 int main() 82 { 83 input(); 84 return 0; 85 }
后面又和lh讨论了A题,大意,给定N<=16个数,进行二进制形式链接(重合部分可以重叠),求连接之后最小的串。我的第一直觉是TSP问题,不过那个最小值和后缀不好维护啊,现在我也不会。后面想想AC自动机维护后最,YY半天不会弄。
【转而搞D】,大意,给定A串和B串,进行操作:删除\插入\替换(编辑距离),然后是可以改变后缀如abcdefg变为abcddddd(显然倒序之后就是改变前序)。
和niuniu讨论了很久,标准的lcs模型,niuniu想不清楚公式:
f[i-1][j] + 1 ->f[i][j](删除第i个)
f[i][j-1] + 1 -> f[i][j](添加第j个)
f[i-1][j-1] + (A[i] != B[j])->f[i][j](不同就change)
估计是脑容量不够用了,一般比赛这个时候都是体力脑力精力透支啊!
我设计了空间和时间都是O(52*N*M),N、M<=500,的算法。大概算了算需要125*10^5个int型,第一反映是会MLE。没写。
最后20分钟,dongfang队走了,lh大仙也跟着走了,我和niuniu闲得没事就搜了搜,发现正解就开了52*N*N的数组。
我坚定的要写D,niuniu在一旁帮我看着(她这个时候应该已经理解转移公式了)
最后还剩下10min,我写完提交MLE,原来数组开成55*555*555了,我改掉成55*51*51提交WA。
后面才发现A串长度和B串不一样,改掉提交WA。
我知道f[k][i][j]也需要考虑前缀变换,于是加上提交,AC! 312min,5A。
太激动了有没有!
然后,我把比赛时间加了30min。掉人品啊!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<algorithm> 5 6 using namespace std; 7 8 const int maxn = 501; 9 char str[maxn]; 10 const int inf = 0x3f3f3f3f; 11 int A[maxn]; 12 int B[maxn]; 13 int n; 14 int m; 15 int dp[maxn][maxn]; 16 int f[53][maxn][maxn]; 17 int get_id(char ch) 18 { 19 if (ch<='Z' && ch>='A') 20 return ch - 'A'; 21 return 26 + ch - 'a'; 22 } 23 24 25 26 void init() 27 { 28 memset(f,-1, sizeof(f)); 29 30 for (int k=0;k<52;k++) 31 { 32 for (int i=0;i<maxn;i++) 33 { 34 f[k][0][i] = i; 35 f[k][i][0] = i; 36 } 37 } 38 39 for (int i=1;i<=n;i++) 40 { 41 for (int j=1;j<=m;j++) 42 { 43 for (int k=0;k<52;k++) 44 { 45 f[k][i][j] = f[k][i-1][j] + 1; 46 f[k][i][j] = min(f[k][i][j], f[k][i-1][j-1] + (k != (int)B[j])); 47 f[k][i][j] = min(f[k][i][j], f[k][i][j-1] + 1); 48 f[k][i][j] = min(f[k][i][j], f[B[j]][i-1][j] + 1 + (k!=B[j])); 49 f[k][i][j] = min(f[k][i][j], f[B[j]][i-1][j-1] + (k!=B[j])); 50 f[k][i][j] = min(f[k][i][j], f[B[j]][i][j-1] + 1 + (k!=B[j])); 51 } 52 } 53 } 54 return ; 55 } 56 int main() 57 { 58 // printf("%d\n",get_id('Z')); 59 while (scanf("%s",str)) 60 { 61 if (str[0]=='#') 62 break; 63 n = strlen(str); 64 for (int i=1;i<=n;i++) 65 { 66 A[i] = get_id(str[n - i]); 67 } 68 scanf("%s",str); 69 m = strlen(str); 70 for (int i=1;i<=m;i++) 71 { 72 B[i] = get_id(str[m-i]); 73 } 74 75 init(); 76 77 78 for (int i=0;i<=n;i++) 79 { 80 for (int j=0;j<=m;j++) 81 { 82 dp[i][j] = inf; 83 } 84 } 85 86 for (int i=0;i<maxn;i++) 87 { 88 dp[0][i] = i; 89 dp[i][0] = i; 90 } 91 for (int i=1;i<=n;i++) 92 { 93 for (int j=1;j<=m;j++) 94 { 95 dp[i][j] = dp[i-1][j] + 1; 96 dp[i][j] = min(dp[i][j], dp[i][j-1] + 1); 97 dp[i][j] = min(dp[i][j], dp[i-1][j-1] + (A[i] != B[j])); 98 int tmp = inf; 99 for (int k=0;k<52;k++) 100 { 101 tmp = min(tmp, 1 + f[k][i][j]); 102 } 103 dp[i][j] = min(dp[i][j], tmp); 104 } 105 } 106 printf("%d\n",dp[n][m]); 107 } 108 return 0; 109 }
总结:
0、我和niuniu迟到近50min,是绝对错误的,以后的比赛,不会再迟到。
1、lh大仙深深的陷入了B题,拉住了全场。I题虽过,掉人品的。
2、所有题目还属于比较轻松的,English要求也不高,我读了其中的B\C\D\F\H\I,其中A\C题没读懂,所有题目都被读了。但是,C\G没有讨论。
3、题目难度属于中等偏下的难度,和省赛难度差不多,多校训练里算最简单的了。
4、目前组队比赛,分工还不明确,讨论不够,比如(lh和niuniu经常都没讨论),而我基本要和两个都讨论;这点,需要加大知识面的交集和题目阅读的交集。
5、niuniu编码能力有待提高,应该有做到自己的算法自己能实现,特别是水题。
6、在图论、DP方面,3个人都应该要多学学,这样才会有讨论的交集,很重要!
7、以后下来还是要多下来讨论总结。
加油!