传送门:ACdream原创群赛(14)の我今天没吃药
这次的题目除了第一题是比赛的时候写出来的外,其他写出来的均为第二天补上的。
PS:这次的题目描述上有很多有意思的地方,细心的可以观察一下。
A:瑶瑶的第K大
给你一串数,要求第K大的数,那么用O(N)的快排就好了,不过这题目有点丧心病狂,最多5000000个数,必须上输入优化了。
#include <stdio.h> int a[5000005]; int n, k, ans; int read () { int x = 0; char ch = ' '; while (ch < '0' || ch > '9') ch = getchar (); while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar (); return x; } void swap (int *x, int *y) { int tmp; tmp = *x; *x = *y; *y = tmp; } void sort (int l, int r) { int last = l; swap (&a[l], &a[(l + r) >> 1]); for (int i = l + 1; i <= r; ++ i) if (a[l] < a[i]) swap (&a[++ last], &a[i]); swap (&a[l], &a[last]); if (last == k) ans = a[k]; else if (k < last) sort (l, last - 1); else sort (last + 1, r); } int main () { while (~scanf ("%d%d", &n, &k)) { for (int i = 1; i <= n; ++ i) a[i] = read (); sort (1, n); printf ("%d\n", ans); } return 0; }
B:瑶瑶饿了
首先确定是有向图,每种食物只能吃一次,一次可以吃任意多。
从起点到终点的花费可以通过最短路来,然后每种食物每次吃任意多我们可以通过将不同份数当作分组背包的元素,每份分别为1、2、3、4...,接下来求一下分组背包就好了
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define Clear(A, X) memset (A, X, sizeof A) #define Min(A, B) ((A) < (B) ? (A) : (B)) #define Max(A, B) ((A) > (B) ? (A) : (B)) typedef long long ll; const int maxN = 10005; const int maxE = 1000000; const int maxQ = 1000000; const int oo = 0x3f3f3f3f; struct Edge { int v, w, n; } edge[maxE]; int adj[maxN], cntE; int d[maxN]; int Q[maxQ], inq[maxN], head, tail; int n, m, V; int cost[maxN], val[maxN], amount[maxN]; ll f[maxN], ans[maxN]; void addedge (int u, int v, int w) { edge[cntE].v = v; edge[cntE].w = w; edge[cntE].n = adj[u]; adj[u] = cntE ++; } void spfa () { Clear (d, oo); Clear (inq, 0); d[0] = 0; head = tail = 0; Q[tail ++] = 0; while (head != tail) { int u = Q[head ++]; inq[u] = 0; for (int i = adj[u]; ~i; i = edge[i].n) { int v = edge[i].v; if (d[v] > d[u] + edge[i].w) { d[v] = d[u] + edge[i].w; if (!inq[v]) { Q[tail ++] = v; inq[v] = 1; } } } } } void work () { int u, v, c; Clear (adj, -1); Clear (cost, 0); Clear (f, 0); Clear (ans, 0); cntE = 0; for (int i = 1; i <= n; ++ i) scanf ("%d%d%d", &amount[i], &val[i], &cost[i]); for (int i = 0; i < m; ++ i) { scanf ("%d%d%d", &u, &v, &c); addedge (u, v, c); } spfa (); for (int i = 1; i <= n; ++ i) { for (int j = 0; j <= V; ++ j) f[j] = ans[j]; for (int v = V; v >= 0; -- v) for (int j = 1; j <= amount[i]; ++ j) if (v - cost[i] * j >= 0) f[v] = Max (f[v], f[v - cost[i] * j] + j * val[i]); else break; for (int j = d[i]; j <= V; ++ j) ans[j] = Max (ans[j], f[j - d[i]]); } printf ("%lld\n", ans[V]); } int main () { scanf ("%d%d%d", &n, &m, &V); work (); return 0; }
C:瑶瑶想要玩滑梯
23333补题解了,这道线段树就是维护左右端点 + 求极值,具体看代码就好了。为了这题特意去看了下线段树,嗷~~~
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std ; #define lson l , m , o << 1 #define rson m + 1 , r , o << 1 | 1 const int maxT = 1000000; const int maxN = 100005; int a[ maxN ] ; int maxo[ maxT ] , maxl[ maxT ] , maxr[ maxT ] ; int numl[ maxT ] , numr[ maxT ] , mark[ maxT ] ; int max ( const int X , const int Y ) { return X > Y ? X : Y ; } int min ( const int X , const int Y ) { return X < Y ? X : Y ; } void PushUp ( int l , int r , int o ) { int m = ( l + r ) >> 1 ; maxo[ o ] = max ( maxo[ o << 1 ] , maxo[ o << 1 | 1 ] ) ; maxl[ o ] = maxl[ o << 1 ] ; maxr[ o ] = maxr[ o << 1 | 1 ] ; numl[ o ] = numl[ o << 1 ] ; numr[ o ] = numr[ o << 1 | 1 ] ; if ( numr[ o << 1 ] < numl[ o << 1 | 1 ] ) { maxo[ o ] = max ( maxo[ o ] , maxr[ o << 1 ] + maxl[ o << 1 | 1 ] ) ; if ( maxl[ o ] == m - l + 1 ) { maxl[ o ] += maxl[ o << 1 | 1 ] ; } if ( maxr[ o ] == r - m ) { maxr[ o ] += maxr[ o << 1 ] ; } } } void PushDown ( int o ) { if ( mark[ o ] ) { mark[ o << 1 ] = mark[ o << 1 | 1 ] = mark[ o ] ; numl[ o << 1 ] = numr[ o << 1 ] = mark[ o << 1 ] ; maxl[ o << 1 ] = maxr[ o << 1 ] = maxo[ o << 1 ] = 1 ; numl[ o << 1 | 1 ] = numr[ o << 1 | 1 ] = mark[ o << 1 | 1 ] ; maxl[ o << 1 | 1 ] = maxr[ o << 1 | 1 ] = maxo[ o << 1 | 1 ] = 1 ; mark[ o ] = 0 ; } } void Build ( int l , int r , int o ) { mark[ o ] = 0 ; if ( l == r ) { maxo[ o ] = maxl[ o ] = maxr[ o ] = 1 ; numl[ o ] = numr[ o ] = a[ l ] ; } else { int m = ( l + r ) >> 1 ; Build ( lson ) ; Build ( rson ) ; PushUp ( l , r , o ) ; } } void Update ( int L , int R , int c , int l , int r , int o ) { if ( L <= l && r <= R ) { maxo[ o ] = maxl[ o ] = maxr[ o ] = 1 ; numl[ o ] = numr[ o ] = mark[ o ] = c ; return ; } PushDown ( o ) ; int m = ( l + r ) >> 1 ; if ( L <= m ) Update ( L , R , c , lson ) ; if ( m < R ) Update ( L , R , c , rson ) ; PushUp ( l , r , o ) ; } int Query ( int L , int R , int l , int r , int o ) { if ( L <= l && r <= R ) return maxo[ o ] ; PushDown ( o ) ; int tmp1 = 0 , tmp2 = 0 , tmp3 = 0 , m = ( l + r ) >> 1 ; if ( L <= m ) tmp1 = Query ( L , R , lson ) ; if ( m < R ) tmp2 = Query ( L , R , rson ) ; if ( L <= m && m < R ) { if ( numr[ o << 1 ] < numl[ o << 1 | 1 ] ) { tmp3 = maxr[ o << 1 ] + maxl[ o << 1 | 1 ] ; } } return max ( tmp1 , max ( tmp2 , tmp3 ) ) ; } void work () { int n , m , L , R , C; char ch[ 5 ] ; scanf ( "%d%d" , &n , &m ) ; for ( int i = 1 ; i <= n ; ++ i ) { scanf ( "%d", &a[i] ) ; } Build ( 1 , n , 1 ) ; for ( int i = 0 ; i < m ; ++ i ) { scanf ( "%s" , ch ) ; if ( ch[ 0 ] == 'U' ) { scanf ( "%d%d%d" , &L , &R , &C ) ; Update ( L , R , C , 1 , n , 1 ) ; } else { scanf ( "%d%d" , &L , &R ) ; int ans = Query ( L , R , 1 , n , 1 ) ; printf ( "%d\n" , ans ) ; } } } int main () { work () ; return 0 ; }
D:瑶瑶的动感光波
看一下数据范围,发现不大,先DFS预处理出每个点的父亲,然后查询的时候每次做一遍O(n * w)的背包就可以了。
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define clear(A, X) memset (A, X, sizeof A) const int maxE = 6010; const int maxN = 3005; struct Edge { int v, n; } edge[maxE]; int adj[maxN], cntE; int pre[maxN]; int n, m, V; int cost[maxN], val[maxN]; int f[maxN], vis[maxN]; int Max (const int X, const int Y) { if (X > Y) return X; return Y; } void addedge (int u, int v) { edge[cntE].v = v; edge[cntE].n = adj[u]; adj[u] = cntE ++; edge[cntE].v = u; edge[cntE].n = adj[v]; adj[v] = cntE ++; } void DFS (int u, int pa) { for (int i = adj[u]; ~i; i = edge[i].n) { int v = edge[i].v; if (v == pa) continue; pre[v] = u; DFS (v, u); } } void work () { int u, v, x, y, z; clear (adj, -1); cntE = 0; for (int i = 1; i <= n; ++ i) scanf ("%d%d", &cost[i], &val[i]); for (int i = 1; i < n; ++ i) { scanf ("%d%d", &u, &v); addedge (u, v); } pre[1] = 0; DFS (1, 0); for (int i = 1; i <= m; ++ i) { scanf ("%d%d%d%d", &z, &x, &y, &V); clear (f, 0); clear (vis, 0); while (!vis[x]) { vis[x] = 1; for (int j = V; j >= cost[x]; -- j) f[j] = Max (f[j], f[j - cost[x]] + val[x]); if(x == z) break; else x = pre[x]; } while (!vis[y]) { vis[y] = 1; for (int j = V; j >= cost[y]; -- j) f[j] = Max (f[j], f[j - cost[y]] + val[y]); if (y == z) break; else y = pre[y]; } printf ("%d\n", f[V]); } } int main () { while (~scanf ("%d%d", &n, &m)) work (); return 0; }E:瑶瑶正式成为CEO
题解说是树链剖分+网络流?反正不会做。。又是一个坑QAQ
F:瑶瑶想找回文串
G:瑶瑶带你玩激光坦克
就是普通的模拟,注意可能会出现循环的情况, 还有读入数据略微注意(不太正常-_-||)。
还有就是最高100万层的递归,改成显式栈就好了。
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define Clear(A, X) memset (A, X, sizeof A) const int maxG = 1005; const int maxS = 1000005; int path[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};//down, up, right, left char G[maxG][maxG]; int sr, sc, n, m; bool vis[maxG][maxG][4], flag[maxG][maxG]; int Max (const int number_1, const int number_2) { if (number_1 > number_2) return number_1; return number_2; } int stop (int nx, int ny, int way) { if (nx < 0 || nx >= n || ny < 0 || ny >= m) return 1; if (G[nx][ny] == '*') return 1; if (vis[nx][ny][way]) return 1; return 0; } int GO (int way) { Clear (vis, 0); Clear (flag, 0); int x = sr, y = sc, nx, ny, ans = 0; while (1) { vis[x][y][way] = 1; if (G[x][y] == '\\' && way == 0) way = 2;//down -> right else if (G[x][y] == '\\' && way == 1) way = 3;//up -> left else if (G[x][y] == '\\' && way == 2) way = 0;//right -> down else if (G[x][y] == '\\' && way == 3) way = 1;//left -> up else if (G[x][y] == '/' && way == 0) way = 3;//down -> left else if (G[x][y] == '/' && way == 1) way = 2;//up -> right else if (G[x][y] == '/' && way == 2) way = 1;//right ->up else if (G[x][y] == '/' && way == 3) way = 0;//left -> down if (G[x][y] == 'E' && !flag[x][y]) ++ ans, flag[x][y] = 1; nx = x + path[way][0]; ny = y + path[way][1]; //printf ("(%d,%d) -> (%d,%d)\n", x, y, nx, ny); if (stop (nx, ny, way)) break; x = nx; y = ny; } //printf ("----------------------------------\n"); return ans; } void work () { char ch; int ans = 0; for (int i = 0; i < n; ++ i) { for (int j = 0; j < m; ++ j) { ch = getchar (); if (ch == 'E' || ch == '.' || ch == '/' || ch == '\\' || ch == 'T' || ch == '*') { G[i][j] = ch; if (G[i][j] == 'T') { sr = i; sc = j; G[i][j] = '.'; } } else -- j; } } for (int i = 0; i < 4; ++ i) ans = Max (ans, GO (i)); printf ("%d\n", ans); } int main () { scanf ("%d%d", &n, &m); work (); return 0; }
本题就是求两个凸包上的线段之间的最近的距离。枚举就好了。
木有模板T T,真的写的有点乱,希望不要介意。
#include <stdio.h> #include <math.h> #include <string.h> #include <algorithm> using namespace std; const int maxN = 1005; const double eps = 1e-8; const double oo = 1e60; struct Node { double x, y; } a[maxN], b[maxN]; int n, m; int dcmp (double X) { return (X > eps) - (X < (-eps)); } double Min (const double X, const double Y) { return X < Y ? X : Y; } double Max (const double X, const double Y) { return X > Y ? X : Y; } double cal (double A, double B, double C, double X, double Y) { return fabs ((A * X + B * Y + C) / sqrt (A * A + B * B)); } double cross (double x1, double y1, double x2, double y2) { return x1 * y2 - x2 * y1; } int check (double X1, double Y1, Node A, Node B, Node C) { int a1 = dcmp (cross (X1, Y1, A.x - B.x, A.y - B.y)); int b1 = dcmp (cross (X1, Y1, A.x - C.x, A.y - C.y)); if (a1 * b1 < 0) return 1; return 0; } double dist (Node A, Node B) { return sqrt ((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y)); } void work () { double ans = oo; for (int i = 1; i <= n; ++ i) scanf ("%lf%lf", &a[i].x, &a[i].y); a[0] = a[n]; scanf ("%d", &m); for (int i = 1; i <= m; ++ i) scanf ("%lf%lf", &b[i].x, &b[i].y); b[0] = b[m]; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) { double A = a[i].y - a[i - 1].y; double B = a[i - 1].x - a[i].x; double C = a[i - 1].y * a[i].x - a[i - 1].x * a[i].y; if (check (A, B, b[j], a[i], a[i - 1])) { ans = Min (ans, cal (A, B, C, b[j].x, b[j].y)); } else ans = Min (ans, Min (dist (a[i], b[j]), dist (a[i - 1], b[j]))); } for (int i = 1; i <= m; ++ i) for (int j = 1; j <= n; ++ j) { double A = b[i].y - b[i - 1].y; double B = b[i - 1].x - b[i].x; double C = b[i - 1].y * b[i].x - b[i - 1].x * b[i].y; if (check (A, B, a[j], b[i], b[i - 1])) { ans = Min (ans, cal (A, B, C, a[j].x, a[j].y)); } else ans = Min (ans, Min (dist (b[i], a[j]), dist (b[i - 1], a[j]))); } printf ("%.4f\n", ans); } int main () { while (~scanf ("%d", &n)) work (); return 0; }
J:瑶瑶说:“这个题目最简单”
据说这题还没出完就放上去了。。。我也先摆上^ U ^
后记:不得不说自己还是太弱了,每一道题都是错了无数次后才写出来的。但是,至少我也写出来了,说明还是有进步的,希望在这次暑假能得到更好的提升!相信自己!