本次训练赛由我、DTen和sssplk完成,三台机子同时进行。
本次训练我们发挥得并不好,只A了一题,但是第一题是很快的1A.然后我就开始卡题,4个小时半卡在B题-矩阵乘法,而DTen则对剩下的题目不知所措,sssplk负责几何也对那道几何无可奈何。这场比赛我能做的题比较多,但是因为坑爹的杭电只提供十分渺小的系统栈,导致我的矩阵二分时递归RE,,然后找各种模板来取代递归版,咳,都是RE.
赛后我一共A了7道题,一道几何比较简单但没去敲,现在说下这场比赛的题目:
2428、Stars 水题。因为是正方形,那么枚举两点,如果它们同线,另外两点的坐标也确定了。用hash来存坐标,然后直接判定那两点是否存在就好,秒杀之。
2429、Word Game 矩阵乘法,给定n个串,当某串的末尾字符等于其他串的首字符时表示他们可以相连,用这些串可以组成一个表示是否能相连的矩阵,也是一个有向图。然后问走不大于n步并且步数为奇数后是目标T串的总方法数。设矩阵是A,那么我们求的就是A^1 + A^3 + ...A^n,再转换变成A1+A1 *( (A^2)^1 + (A^2)^2...(A^2)^k),然后跑矩阵乘法n…n^3log(k)的复杂度解决。杭电改版后系统栈特别小,这题不能用递归式算法,并未那个k特别大。详细解题报告见Here.
2430、Beans 很多方法都可以过,我用单调队列做。有n坨豌豆,每坨都有w[i]个,现在要从中选择连续的若干坨,然后用一个能装p个豌豆的背包装豆子,直到剩下的豌豆数量<=p为止,还要求剩下的豌豆数量少于k,问最多能装多少包。那么选择连续的一段和为sum,使得max(sum/p) 且sum%p<=k,这个sum可以用sum[i] - sum[j]来表示,那么确定了i,就是找最小的j使得(sum[i] - sum[j]) % p <= k,这时和最大。将每个和对p取余得到一个余数r数组,然后按余数大小优先位置其次的顺序排序。排完序之后就可以根据ri - k <= rj <= ri来往前找那个最小的j。遍历数组r,因为r是有序的,我用单调队列储存r小于当前的最小下标。详细解题报告见Here
2431、Counting Problem 很难转换模型,但是转换后就是水题,训练没做出来,智商是硬伤啊。每次固定左上方边长为n-2,n-3...n-k的子正方形(n-k >= k),剩下边长为2,3....k的子正方形随意放,设答案是dp[n],dp[n] = dp[n-2] + dp[n-3] +..dp[n-k] (n-k<=k).这式子看起来不好写,但实际上用两个for循环就可以,第一位表示这个k,然后每次都加上dp[j-i]。具体见代码。
2432、Farey Sequence Again 数论。第一次听到这种法雷级数,算见了市面。因为k<n这个条件的约束使得本题特别简单,因为小于n个的话分子要么是1,要么是2,要么是3,为什么,打表打几个看下然后YY即可得。得到上面的yy结论后,用O(1)的复杂度判断到底第k个法雷级数分子是1还是2还是3,n个可以分成1/2,1/3,以及剩下来的,1/2并不是严格的,而是(1+n) / 2,1/3是(n+2)/3,也就是上取整。第1部分分子是1,第2部分分子是2,第3部分比较难想,有1有2有3,那么将分子搞成6的话分母又是连续的,每6个连续的分母里面有2个是和6互质,那么就有4个分母可以进行约分。这样就找第几个4以及判断是4个中的第几个即可。
2433、Travel 很好的搜索题。给定n个点m条边的无向连通图,Sum表示每个点到其他所有点的最小距离和,问我们删去某条边之后的Sum。朴素做法是枚举每条边,然后进行n次BFS,复杂度是O(n*m^2)。这个复杂度太高了,我们为了降低复杂度把枚举边转换成和点相关的操作,那么复杂度就有可能是O(n^2*m),这样就应该能过。我们先预处理,做n次BFS,把每个点到其他所有点的最短距离求出来,并求出都经过哪些边,叫它关键边,如果某条边是这点的关键边,那么就要更新一次,再做一次BFS,否则直接用之前预处理求出的Sum即可。本题很值得学习的是这个预处理,我觉得和炮兵阵地的预处理一样漂亮,极大地降低了复杂度。
2437、Jerboas 拓朴排序+DP,好题。给定一个n个点m条边的有向无环图,每个点表示一个洞,洞有临时和永久之分,从临时洞开始找一个永久洞结束,要求距离为k的倍数并要求距离最小。也就是说我们要找一个点,他的距离对k取模余数为0并且除数最小,这样到每个点最多1000种状态,我们设个Dp[i][j],表示到i点状态j时对k的最小除数dp[i][j]*k+j就是最小距离。这样状态转移方程就是: Dp[next][j+(j+len)%k] = min(Dp[next][j+(j+len)%k],Dp[i][j]+(j+len)/k)(i有边连向next,边权为len),复杂度是O(n*m)
本次训练状态确实不好,很多题目并不难,看到那些约束条件就可以轻松地转换模型,然后就可以做了。卡题卡得死死的,悲剧了...下不为例,一题最多卡1个半小时。
这次比赛还是学到挺多东西,比如法雷级数,比如当复杂度过高时考虑预处理,比如枚举某个变量复杂度过高时考虑枚举另一个变量,还有J题我也很喜欢,喜欢那种表示状态的方式。
最后给源代码。后面的三题过几天再做,网络流没学,模拟题又巨恶心。
A :
#include <stdio.h> #include <string.h> #include <map> #include <stack> #include <vector> #include <queue> #include <algorithm> using namespace std; #define MAX 1200 struct node { int x,y; }arr[MAX]; int n,m,ans,cnt; bool vis[MAX*2][MAX*2]; map<int,int> hash; int cmp(node a,node b) { if (a.x == b.x) return a.y > b.y; else return a.x > b.x; } int main() { int i,j,k,t; scanf("%d",&t); while (t--) { scanf("%d",&n); ans = cnt = 0; hash.clear(); memset(vis,false,sizeof(vis)); for (i = 1; i <= n; ++i) { scanf("%d%d",&arr[i].x,&arr[i].y); //hash[arr[i]] = ++cnt; if (hash.find(arr[i].x) == hash.end()) hash[arr[i].x] = ++cnt; if (hash.find(arr[i].y) == hash.end()) hash[arr[i].y] = ++cnt; vis[hash[arr[i].x]][hash[arr[i].y]] = true; } sort(arr+1,arr+1+n,cmp); for (i = 1; i <= n; ++i) for (j = i + 1; j <= n; ++j) if (arr[i].x == arr[j].x){ int len = arr[i].y - arr[j].y; len = abs(len); node cur; cur.x = arr[i].x + len; cur.y = arr[i].y; if (hash.find(cur.x) != hash.end() && hash.find(cur.y) != hash.end()) { if (vis[hash[cur.x]][hash[cur.y]] == 0) continue; cur.x = arr[j].x + len; cur.y = arr[j].y; if (hash.find(cur.x) != hash.end() && hash.find(cur.y) != hash.end()) if (vis[hash[cur.x]][hash[cur.y]] == 1) ans++; } } printf("%d\n",ans); } }
B:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <stdio.h> #include <string.h> #define MAX 100 #define MOD 10001 #define int64 int//long long//__int64 int n, begin, end; int64 m,final[MAX][MAX]; char str[MAX][MAX],tp[MAX]; struct Mat { int mat[MAX][MAX], size; Mat() {size = MAX;}; Mat(int n) { size = n; memset(mat, 0, sizeof (mat)); } friend Mat operator *(Mat a, Mat b); friend Mat operator +(Mat a, Mat b); friend Mat operator ^(Mat a, int64 k); } E; Mat operator *(Mat a, Mat b) { Mat c; c.size = a.size; memset(c.mat, 0, sizeof (c.mat)); for (int i = 0; i < c.size; ++i) for (int j = 0; j < c.size; ++j) for (int k = 0; k < c.size; ++k) if (a.mat[i][k] && b.mat[k][j]) c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD; return c; } Mat operator +(Mat a, Mat b) { Mat c; c.size = 2 * n; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) c.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % MOD; return c; } Mat operator ^(Mat a, int64 k) { Mat c = E; c.size = a.size; while (k) { if (k & 1) c = c * a; a = a * a, k >>= 1; } return c; } Mat sum1(Mat A,int k) { //空间复杂度为O(n*n),时间复杂度为O(log(k)*n^3) if (k == 1) return A; if (k & 1) return sum1(A,k-1) + (A^k); return sum1(A,k/2) * ((A^(k / 2)) + E); } Mat sum2(Mat A, int64 k) { int i,j; Mat c = A; c.size = 2 * A.size; for (i = 0; i < n; ++i) c.mat[i][i+n] = c.mat[i+n][i+n] = 1; c = c^(k+1); Mat temp; temp.size = n; for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) temp.mat[i][j] = (i==j?c.mat[i][j+n]-1:c.mat[i][j+n]); return temp; } int Solve() { int i, j; Mat c; E.size = c.size = n; memset(c.mat, 0, sizeof (c.mat)); memset(E.mat, 0, sizeof (E.mat)); for (i = 0; i < 2 * n; ++i) E.mat[i][i] = 1; for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) if (str[i][strlen(str[i]) - 1] == str[j][0]) c.mat[i][j] = 1; if (m % 2 == 0) m--; int cnt = (m - 1) / 2; Mat c2 = c * c; int ans = 0; c = c + c * sum2(c2,cnt); ans = (ans + c.mat[begin][end]) % MOD; return ans; } int main() { int i, j, k,t; scanf("%d", &t); while (t--) { scanf("%d", &n); for (i = 0; i < n; ++i) scanf("%s",str[i]); scanf("%s",tp); for (i = 0; i < n; ++i) if (strcmp(tp,str[i]) == 0) begin = i; scanf("%s",tp); for (i = 0; i < n; ++i) if (strcmp(tp,str[i]) == 0) end = i; scanf("%d",&m); int ans = Solve(); printf("%d\n",ans%MOD); } }
C:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define MAX 1000010 #define max(a,b) (a)>(b)?(a):(b) struct node { int x,in; }r[MAX]; int sum[MAX],mmin[MAX]; int n,p,k,ans,hash[MAX]; int cmp(node a,node b) { if (a.x == b.x) return a.in > b.in; else return a.x < b.x; } int main() { int i,j,t; int cas = 0,mmin; scanf("%d",&t); while (t--) { scanf("%d%d%d",&n,&p,&k); for (i = 1; i <= n; ++i) { scanf("%d",&sum[i]); r[i].in = i; sum[i] += sum[i-1]; r[i].x = sum[i] % p + k; } sort(r+1,r+1+n,cmp); //for (i = 1; i <= n; ++i) // printf("%d in = %d\n",r[i].x,r[i].in); mmin = n + 1; for (i = n; i >= 1; --i) { hash[r[i].in] = i; int temp = r[i].in; r[i].in = mmin; if (temp < mmin) mmin = temp; } //for (i = 1; i <= n; ++i) //printf("i = %d r[i] = %d\n",i,r[i].in); ans = -1; for (i = 1; i <= n; ++i) { int index = r[hash[i]].in; if (sum[i] % p <= k) ans = max(ans,sum[i] / p); if (r[index].in < i) ans = max(ans,(sum[i] - sum[r[index].in])/ p); //printf("i = %d ans = %d\n",i,ans); } printf("Case %d: %d\n",++cas,ans); } }
D:
#include <stdio.h> #include <string.h> #define MAX 510 #define MOD 1000007 int main() { int i,j,k,n,t; int dp[MAX] = {0}; dp[0] = 1; for (i = 2; i < MAX; ++i) for (j = i; j < MAX; ++j) dp[j] = (dp[j] + dp[j-i]) % MOD; scanf("%d",&t); while (t--) { scanf("%d",&n); printf("%d\n",dp[n]); } }
E:
#include <stdio.h> #include <string.h> int Gcd(int x,int y) { int r = x % y; while (r) { x = y,y = r; r = x % y; } return y; } int main() { int i,j,k,t; int n,rest,bigest; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&k); rest = (n + 1) / 2;//上取整 if (k <= rest) { //前半部分,分子为1,分母递减 printf("1/%d\n",n-k+1); continue; } k = k - rest; rest = (n + 2) / 3; if (k <= rest) { //中间部分,分子为2,分母递减 j = Gcd(2,n-k+1); printf("%d/%d\n",2/j,(n-k+1)/j); continue; } k = k - rest; rest = (k - 1) % 4 + 1; bigest = n * 2 - (k - 1) / 4 * 6; while (1) { j = Gcd(6,bigest); if (j == 1) { //最大公约数为1,不是法雷级数 bigest--; continue; } else if (rest == 1) { //最后一个法雷级数 printf("%d/%d\n",6/j,bigest/j); break; } bigest--,rest--; } } }
F:
#include <stdio.h> #include <string.h> #include <vector> #include <algorithm> using namespace std; #define MIN 110 #define MAX 3300 struct bnode { //广搜用的node int dis,v; }qu[MAX]; struct mnode { //建图用的node int v,in; mnode *next; }map[MAX*2],*Head[MIN]; bool vis[MIN][MAX],used[MIN][MAX];//广搜用的判点是否搜过,判某点是否在最小路径树上 int x[MAX],y[MAX],ans,flag; int ptr,n,m,sum[MAX],head,tail; void Initial() { ptr = 0; memset(vis,0,sizeof(vis)); memset(used,0,sizeof(used)); memset(Head,NULL,sizeof(Head)); } void AddEdge(int x,int y,int z) { map[ptr].v = y,map[ptr].in = z; map[ptr].next = Head[x],Head[x] = &map[ptr++]; } int Bfs_AC(int cur,int index) { int cnt = 0; bnode now,next; sum[index] = 0; head = tail = 0; now.v = cur,now.dis = 0; qu[++head] = now; vis[index][cur] = true; while (tail < head) { cnt++; now = qu[++tail]; sum[index] += now.dis; mnode *p = Head[now.v]; while (p != NULL) { int v = p->v,in = p->in; if (!vis[index][v] && !used[index][in]) { next.v = v; vis[index][v] = true; used[index][in] = true; next.dis = now.dis + 1; qu[++head] = next; } p = p->next; } } return cnt; } int main() { int i,j,k,t,ta,tb; while (scanf("%d%d",&n,&m) != EOF) { Initial(); for (i = 1; i <= m; ++i) { scanf("%d%d",&x[i],&y[i]); AddEdge(x[i],y[i],i),AddEdge(y[i],x[i],i); } for (i = 1; i <= n; ++i) Bfs_AC(i,i); //Debug_Input for (j = 1; j <= m; ++j) { ans = flag = 0; for (i = 1; i <= n && !flag; ++i) { if (!used[i][j]) ans += sum[i]; else { memset(vis[n+1],false,sizeof(vis[n+1])); memset(used[n+1],false,sizeof(used[n+1])); used[n+1][j] = true; ta = Bfs_AC(i,n+1); if (ta < n) flag = 1; else ans += sum[n+1]; } } if (flag) printf("INF\n"); else printf("%d\n",ans); } } }
J:
#include <stdio.h> #include <string.h> #include <vector> using namespace std; #define MAX 1001 #define INF 2147483647 #define min(a,b) ((a)<(b)?(a):(b)) struct node { int v,len; }cur,next,qu[MAX]; vector<node> map[MAX]; char str[MAX]; int top,st[MAX],cnt; int count[MAX],topo[MAX]; int s,k,n,m,ans,ansi,dp[MAX][MAX]; void Initial() { ansi = ans = -1; for (int i = 0; i <= n; ++i) { map[i].clear(); count[i] = 0; for (int j = 0; j <= k; ++j) dp[i][j] = INF; } } void ToooPooo() { int i,j,v,size; cnt = top = 0; for (i = 1; i <= n; ++i) if (count[i] == 0) st[++top] = i; while (top > 0) { i = st[top--]; topo[++cnt] = i; size = map[i].size(); for (j = 0; j < size; ++j) { cur = map[i][j]; count[cur.v]--; if (count[cur.v] == 0) st[++top] = cur.v; } } } void Debug_Input() { int i,j,t; for (i = 1; i <= n; ++i) for (j = 0; j < k; ++j) printf("i = %d j = %d dp[i][j] = %d\n",i,j,dp[i][j]); } int Solve_AC() { int i,j,t,size,v,tp; dp[s][0] = 0; for (i = 1; i <= cnt; ++i) { v = topo[i]; size = map[v].size(); for (j = 0; j < size; ++j) { next = map[v][j]; for (t = 0; t < k; ++t) if (dp[v][t] != INF) { tp = (t + next.len) % k; dp[next.v][tp] = min(dp[next.v][tp],dp[v][t] + (t+next.len)/k); } } } for (i = 1; i <= n; ++i) if (str[i] == 'P' && dp[i][0] != INF && (ans == -1 || dp[i][0] * k < ans)) ansi = i,ans = dp[i][0] * k; return ansi; } int main() { int i,j,t; int cas = 0,a,b,c; scanf("%d",&t); while (t--) { scanf("%d%d%d%d",&n,&m,&s,&k); scanf("%s",str+1); Initial(); for (i = 1; i <= m; ++i) { scanf("%d%d%d",&a,&b,&c); count[b]++; cur.v = b,cur.len = c; map[a].push_back(cur); } ToooPooo(); Solve_AC(); printf("Case %d: %d %d\n",++cas,ans,ansi); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。