USTC Monthly Contest 2011-03-06

链接: http://acm.ustc.edu.cn/ustcoj/contest.php?contest=10

 

A题:图论题,将剩余的空位置看成一个节点,任何相邻的两点(都没有被占)建立一条无向边,问题可以转化为 一般图的最大匹配了,可以采用带花树算法实现求解。这里提供武汉大学 momodi 大牛的一个手写模板

 

一般图的最大匹配之带花树算法

 

//author: momodi@whuacm struct Graph { int n, match[maxn]; bool adj[maxn][maxn]; void clear() { memset(adj, 0, sizeof(adj)); n = 0; } void insert(const int &u, const int &v) { get_max(n, max(u, v) + 1); adj[u][v] = adj[v][u] = 1; } int max_match() { memset(match, -1, sizeof(match)); int ans = 0; for (int i = 0; i < n; ++i) { if (match[i] == -1) { ans += bfs(i); } } return ans; } int Q[maxn], pre[maxn], base[maxn]; bool hash[maxn]; bool in_blossom[maxn]; int bfs(int p) { memset(pre, -1, sizeof(pre)); memset(hash, 0, sizeof(hash)); for (int i = 0; i < n; ++i) { base[i] = i; } Q[0] = p; hash[p] = 1; for (int s = 0, t = 1; s < t; ++s) { int u = Q[s]; for (int v = 0; v < n; ++v) { if (adj[u][v] && base[u] != base[v] && v != match[u]) { if (v == p || (match[v] != -1 && pre[match[v]] != -1)) { int b = contract(u, v); for (int i = 0; i < n; ++i) { if (in_blossom[base[i]]) { base[i] = b; if (hash[i] == 0) { hash[i] = 1; Q[t++] = i; } } } } else if (pre[v] == -1) { pre[v] = u; if (match[v] == -1) { argument(v); return 1; } else { Q[t++] = match[v]; hash[match[v]] = 1; } } } } } return 0; } void argument(int u) { while (u != -1) { int v = pre[u]; int k = match[v]; match[u] = v; match[v] = u; u = k; } } void change_blossom(int b, int u) { while (base[u] != b) { int v = match[u]; in_blossom[base[v]] = in_blossom[base[u]] = true; u = pre[v]; if (base[u] != b) { pre[u] = v; } } } int contract(int u, int v) { memset(in_blossom, 0, sizeof(in_blossom)); int b = find_base(base[u], base[v]); change_blossom(b, u); change_blossom(b, v); if (base[u] != b) { pre[u] = v; } if (base[v] != b) { pre[v] = u; } return b; } int find_base(int u, int v) { bool in_path[maxn] = {}; while (true) { in_path[u] = true; if (match[u] == -1) { break; } u = base[pre[match[u]]]; } while (!in_path[v]) { v = base[pre[match[v]]]; } return v; } }; 

 

B题:数学题,题意大致是,给定三个数 m、a、b,求出这样的两个数 w 和 h,满足 w * h <= m,a / b <= w / h <= 1,而且保证让w 和 h的乘积最大,可以采用先建立素数表,枚举w,二分 h

 

#include<stdio.h> #include<string.h> #include<queue> #include<iostream> #include<algorithm> using namespace std; const int maxn = 100000; long long prime[10000]; bool tag[maxn]; int tot = 0; void init() { memset(tag, 0, sizeof(tag)); prime[tot++] = 2LL; for(int i = 3; i < maxn; i += 2) { if( !tag[i] ) { prime[tot++] = (long long)i; for(int j = i + i; j < maxn; j += i) { tag[j] = 1; } } } //printf("tot = %d/n", tot); } long long m, a, b; int main() { init(); while( scanf("%lld %lld %lld", &m, &a, &b) == 3 && (m + a + b) ) { long long w = -1, h = -1, l, r, mid; for(int i = 0; i < tot; ++i) { if( prime[i] * prime[i] > m) break; l = i, r = tot - 1; while(l <= r) { mid = (l + r) >> 1; if(prime[i] * prime[mid] > m ) { r = mid - 1; continue; } if( b * prime[i] < a * prime[mid] ) { r = mid - 1; continue; } if(w == -1 || w * h < prime[i] * prime[mid]) { w = prime[i]; h = prime[mid]; } l = mid + 1; } } printf("%lld %lld/n", w, h); } return 0; } 

 

C题:尚未解出

 

D题:模拟题,高精度加法

 

import java.math.BigInteger; import java.util.Scanner; import javax.naming.BinaryRefAddr; public class Main { public static void main(String[] args) { Scanner cin = new Scanner(System.in); BigInteger a = new BigInteger("0"); BigInteger b = new BigInteger("0"); while (cin.hasNext()) { a = cin.nextBigInteger(); b = cin.nextBigInteger(); System.out.println(a.add(b).toString()); } } } 

 

E题:尚未解出

 

F题:尚未解出

 

G题:网络流,一开始想到了最大流,但是看到点那么多,以为会超时,没写。建立 N + 5 * N + C + 2 个节点,其中,N 表示 学生,5 * N 表示 N 个人的时间(5天),C 表示课程, 2 表示源点和汇点,把握好对应的边权,需要手写建立 hash 函数,或者直接调用 map

 

#include<stdio.h> #include<string.h> #include<queue> #include<string> #include<vector> #include<map> #include<iostream> #include<algorithm> using namespace std; const int maxV = 6000; const int maxE = 3000000; const int inf = 0x7fffffff; struct node { int v, idx, w; node *nxt; }; node *G[maxV]; node E[maxE]; int nE = 0; void init() { memset(G, 0, sizeof(G)); nE = 0; } void add(int u, int v, int w) { E[nE].v = v, E[nE].idx = nE, E[nE].w = w; E[nE].nxt = G[u], G[u] = &E[nE++]; E[nE].v = u, E[nE].idx = nE, E[nE].w = 0; E[nE].nxt = G[v], G[v] = &E[nE++]; } int s, t; // source, sink int h[maxV]; bool bfs() { memset(h, -1, sizeof(h)); //queue<int> q; q.push(s); static int Q[maxV]; int Qs = 0, Qt = 0; Q[Qt++] = s; h[s] = 0; int u; while( Qs != Qt ) { u = Q[Qs++]; if(Qs == maxV - 1) Qs = 0; for(node *e = G[u]; e; e = e->nxt) { if( (e->w) > 0 && h[e->v] == -1 ) { h[e->v] = h[u] + 1; Q[Qt++] = e->v; if(Qt == maxV - 1) Qt = 0; } } } return h[t] != -1; } int dfs(int &curV, int curF) { if(curV == t) return curF; int cntf = 0, tmpf; for(node *e = G[curV]; e; e = e->nxt) { if( (e->w > 0) && (h[e->v] == h[curV] + 1) && (cntf < curF) && ( (tmpf = dfs(e->v, min(e->w, curF - cntf))) > 0 ) ) { e->w -= tmpf; E[(e->idx) ^ 1].w += tmpf; cntf += tmpf; } } if(cntf == 0) h[curV] = -1; return cntf; } int C, N; struct info { int T, tot; }; info A[1400]; map<string, int> LocA; map<string, int> T, _T; int K, X; char cName[18], cTime[18]; char fName[700][18]; int fL[700]; bool tag[5]; int offset; int main() { T["Monday"] = 0, T["Tuesday"] = 1, T["Wednesday"] = 2, T["Thursday"] = 3, T["Friday"] = 4; while( scanf("%d", &C) == 1) { init(); LocA.clear(); _T.clear(); for(int i = 0; i < C; ++i) { scanf("%s %s %d", cName, cTime, &A[i].tot); A[i].T = T[ cTime ]; _T[ cName ] = A[i].T; LocA[ cName ] = i; } scanf("%d", &N); if(N == 0 && C == 0) break; s = N + 5 * N + C, t = s + 1; for(int i = 0; i < C; ++i) { add(N + 5 * N + i, t, A[i].tot); } int ans = 0, sgn = 1, tmp; for(int i = 0; i < N; ++i) { scanf("%d %d", &K, &X); if(X > 5 || K < X) sgn = 0; ans += X; if( sgn == 0 ) for(int j = 0; j < K; ++j) scanf("%s", cName); else { for(int j = 0; j < K; ++j) { scanf("%s", fName[j]); fL[j] = LocA[ fName[j] ]; } //memset(tag, 0, sizeof(tag)); //for(int j = 0; j < K; ++j) tag[ A[ fL[j] ].T ] = 1; //for(int j = 0; j < 5; ++j) if( tag[j] ) add(i, N + j, 1); for(int j = 0; j < 5; ++j) add(i, N + 5 * i + j, 1); for(int j = 0; j < K; ++j) add(N + 5 * i + A[ fL[j] ].T, N + 5 * N + fL[j], 1); } if( sgn ) add(s, i, X); } if( !sgn ) { puts("No."); continue; } while( bfs() ) ans -= dfs(s, 0x7fffffff); printf("%s/n", ans ? "No." : "Yes!"); } return 0; } 

 

H题:矩阵 + 二分,这是一道很好的矩阵题目,关键在于如何建立矩阵,不妨先做做 POJ3233 题,那道题的一些结论对于求解该题有帮助。这题需要把握好细节。

 

#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<queue> #include<vector> using namespace std; const int maxN = 50; typedef long long i64d; i64d _M, _N, _P, K; struct Matrix { i64d M[maxN][maxN]; }; void MUL(Matrix A, Matrix B, Matrix &C, i64d &X_X) { for(i64d i = 0; i < X_X; ++i) { for(i64d j = 0; j < X_X; ++j) { C.M[i][j] = 0; for(i64d k = 0; k < X_X; ++k) { if( !A.M[i][k] || !B.M[k][j] ) continue; C.M[i][j] += (A.M[i][k] * B.M[k][j]) % _P; C.M[i][j] %= _P; } } } } void POWER(Matrix &x, i64d y, Matrix &E, i64d &X_X) { if(y == 1LL || y == 0) return ; POWER(x, y >> (0x1), E, X_X); MUL(x, x, x, X_X); if( y & (0x1) ) MUL(x, E, x, X_X); } i64d B[20], C[20]; i64d SUM(i64d X) { if( X <= K ) { i64d ans = 0; for(i64d i = 0; i < X; ++i) { ans += B[i]; // need not to add "% _P" ans %= _P; } return ans; } i64d ans = 0; for(i64d i = 0; i < K; ++i) { ans += B[i]; // need not to add "% _P" ans %= _P; } //printf("ans = %lld/n", ans); Matrix E; memset(E.M, 0, sizeof(E.M)); for(i64d i = 0; i < K; ++i) { for(i64d j = 0; j < K; ++j) { if(i == 0) E.M[i][j] = C[j]; if(i == j + 1LL) E.M[i][j] = 1LL; if(i == j) { E.M[i + K][j + K] = 1LL; E.M[i][j + K] = 1LL; } } } Matrix x = E; i64d X_X = 2LL * K; POWER(x, X - K + 1LL, E, X_X); for(i64d i = 0; i < K; ++i) { x.M[i][i + K] -= 1LL; } for(i64d j = 0; j < K; ++j) { ans += (x.M[0][j + K] * B[K - j - 1]) % _P; ans %= _P; } return ans; } int main() { while( scanf("%lld", &K) == 1 ) { for(i64d i = 0; i < K; ++i) scanf("%lld", B + i); for(i64d i = 0; i < K; ++i) scanf("%lld", C + i); scanf("%lld %lld %lld", &_M, &_N, &_P); for(i64d i = 0; i < K; ++i) B[i] %= _P, C[i] %= _P; i64d ans_M = SUM( _M - 1LL ), ans_N = SUM( _N ); //printf("ans_M = %lld, ans_N = %lld/n", ans_M, ans_N); printf("%lld/n", ((ans_N - ans_M) % _P + _P) % _P); } return 0; } 

 

2011-03-08 by hzwu.

你可能感兴趣的:(USTC Monthly Contest 2011-03-06)