zoukankan      html  css  js  c++  java
  • 2020杭电多校第二场题解

    2020 Multi-University Training Contest 2


    1001 Total Eclipse

    并查集。由于每次选择最大的连通块,所以连通块每次选择最小的点,删除后选择新的连通块组继续操作。

    对于每个连通块,用并查集反向处理连通块即可。

    • 将当前最大的点加入图,并删除该点,连通块数加一
    • 遍历该点的边,每与一个不同的集合合并,连通块数减一
    • 每次贡献为连通块数 * (该点的权值 - 下一个点的权值)

    本来应该是签到题结果最后一小时才出,而且还WA3。

    确实是思维不足,把题目想得太复杂了,而且队友错误思想很容易形成干扰。

    这种题目最好还是多独立思考,或许冷静后很快就能想出。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    typedef pair<int, int> pii;
    const int maxn = 1e5 + 5;
    int b[maxn], f[maxn];
    bool vis[maxn];
    vector<int> E[maxn];
    vector<int> V[maxn];
    
    int main() {
    	int t;
    	scanf("%d", &t);
    
    	function<int(int)> find;
    	find = [&](int x)->int {
    		while (x != f[x])
    			x = f[x] = f[f[x]];
    		return x;
    	};
    
    	while (t--) {
    		int n, m;
    		scanf("%d%d", &n, &m);
    		for (int i = 1; i <= n; i++) {
    			scanf("%d", &b[i]);
    			E[i].resize(0);
    			V[i].resize(0);
    			vis[i] = false;
    			f[i] = 0;
    		}
    		int u, v;
    		for (int i = 1; i <= m; i++) {
    			scanf("%d%d", &u, &v);
    			E[u].push_back(v);
    			E[v].push_back(u);
    		}
    
    		int k = 0;
    		function<void(int)> dfs;
    		dfs = [&](int now) {
    			vis[now] = true;
    			V[k].push_back(now);
    			for (auto it : E[now]) {
    				if (vis[it]) continue;
    				dfs(it);
    			}
    		};
    
    		LL ans = 0;
    		for (int i = 1; i <= n; i++) {
    			if (vis[i]) continue;
    			k++;
    			dfs(i);
    			V[k].push_back(0);
    			sort(V[k].begin(), V[k].end(), [&](const int o1, const int o2) {
    				return b[o1] > b[o2];
    			});
    			LL cnt = 0;
    			for (int j = 0; j < (int)V[k].size();) {
    				if (!V[k][j]) break;
    				int p = j;
    				while (++j < (int)V[k].size() && V[k][j] == V[k][j - 1])
    					;
    				for (int g = p; g < j; g++) {
    					int now = V[k][g];
    					f[now] = now;
    					cnt++;
    					for (auto it : E[now]) {
    						if (!f[it])
    							continue;
    						if (find(now) != find(it))
    							f[find(now)] = find(it),
    							cnt--;
    					}
    				}
    				ans = ans + cnt * (b[V[k][p]] - b[V[k][j]]);
    			}
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    1005 New Equipments

    只要求出每个二次函数上最小 (n) 个点的值,得到 (n^2) 个点,然后进行匹配就能得到最优解。
    可以使用最小费用最大流求解问题,只要根据点所在函数和横坐标建边,跑 (n) 次最大流即可。

    最小费用最大流模板题,比赛时没时间开题,还是前期被卡后期时间不足的问题。

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <queue>
    #define SZ(v) (int)v.size()
    #define pii pair<ll,ll>
    #define fi first
    #define se second
    #define ll long long
    using namespace std;
    const ll INF = 1e16;
    const int maxn = 3010;
    const int maxm = 100010;
    struct MCMF {
    	struct Edge {
    		ll v, cap, cost, rev;
    	};
    	ll flow, cost, s, t, n;
    	ll dist[maxn], H[maxn], pv[maxn], pe[maxn];
    	std::vector<Edge> G[maxn];
    	bool dijkstra() {
    		std::priority_queue<pii, std::vector<pii>, std::greater<pii> > q;
    		std::fill(dist, dist + n + 1, INF);
    		dist[s] = 0; q.push({ 0, s });
    		while (!q.empty()) {
    			pii x = q.top(); q.pop();
    			ll& u = x.se;
    			if (dist[u] < x.fi) continue;
    			for (int i = 0; i < SZ(G[u]); ++i) {
    				Edge& e = G[u][i];
    				ll& v = e.v;
    				pii y(dist[u] + e.cost + H[u] - H[v], v);
    				if (e.cap > 0 && dist[v] > y.fi) {
    					dist[v] = y.fi;
    					pe[v] = i, pv[v] = u;
    					q.push(y);
    				}
    			}
    		}
    
    		if (dist[t] == INF) return false;
    		for (int i = 0; i <= n; ++i) H[i] += dist[i];
    
    		ll f = INF;
    		for (int v = t; v != s; v = pv[v]) f = std::min(f, G[pv[v]][pe[v]].cap);
    
    		flow += f;
    		cost += f * H[t];
    
    		for (int v = t; v != s; v = pv[v]) {
    			Edge& e = G[pv[v]][pe[v]];
    			e.cap -= f;
    			G[v][e.rev].cap += f;
    		}
    
    		return true;
    	}
    	void solve(int s, int t) {
    		this->s = s, this->t = t;
    		flow = cost = 0;
    		std::fill(H, H + n + 1, 0);
    		while (dijkstra());
    	}
    	void ctu() {
    		while (dijkstra());
    	}
    	void init(int n) {
    		this->n = n;
    		for (int i = 0; i <= n; ++i) G[i].clear();
    	}
    	void addEdge(int u, int v, int cap, ll cost) {
    		G[u].push_back({ v, cap, cost, SZ(G[v]) });
    		G[v].push_back({ u, 0, -cost, SZ(G[u]) - 1 });
    	}
    
    } mcmf;
    struct point {
    	ll x, y, id;
    	point() {}
    	point(ll x_, ll y_, ll id_) : x(x_), y(y_), id(id_) {}
    	bool operator < (const point& k) const {
    		return y == k.y ? x < k.x : y < k.y;
    	}
    };
    int n; ll m;
    ll a[55], b[55], c[55];
    vector<point> p;
    int uni[maxn];
    ll ans[55];
    int main() {
    	int t; scanf("%d", &t);
    	while (t--) {
    		scanf("%d %lld", &n, &m);
    		for (int i = 0; i < n; ++i) scanf("%lld %lld %lld", &a[i], &b[i], &c[i]);
    		p.clear();
    		int tot = n;
    		tot += 2;
    		for (int i = 0; i < n; ++i) {
    			double dx = -0.5 * b[i] / a[i];
    			int l = floor(dx), r = ceil(dx);
    			if (l == r) ++r;
    			if (l > m) l = m;
    			if (r <= 0) r = 1;
    			int cnt = tot;
    			while (cnt > 0) {
    				if (l > 0) {
    					p.emplace_back(point(l, a[i] * l * l + b[i] * l + c[i], i));
    					--l; --cnt;
    				}
    				if (r <= m) {
    					p.emplace_back(point(r, a[i] * r * r + b[i] * r + c[i], i));
    					++r; --cnt;
    				}
    				if (l <= 0 && r > m) break;
    			}
    		}
    		sort(p.begin(), p.end());
    		int s = 0, ed = 3000;
    		mcmf.init(ed + 1);
    		for (int i = 0; i < p.size(); i++) {
    			uni[i] = p[i].x;
    		}
    		int cntt = p.size();
    		sort(uni, uni + cntt);
    		cntt = unique(uni, uni + cntt) - uni;
    		for (int i = 0; i < p.size(); i++) {
    			p[i].x = lower_bound(uni, uni + cntt, p[i].x) - uni;
    		}
    		for (int i = 1; i <= n; i++) {
    			mcmf.addEdge(1, 1 + i, 1, 0);
    		}
    		for (int i = 0; i < p.size(); i++) {
    			mcmf.addEdge(1 + p[i].id + 1, n + 2 + p[i].x, 1, p[i].y);
    		}
    		for (int i = 0; i < cntt; i++) {
    			mcmf.addEdge(i + n + 2, 3000, 1, 0);
    		}
    		for (int i = 0; i < n; i++) {
    			mcmf.addEdge(0, 1, 1, 0);
    			if (i == 0) mcmf.solve(s, ed);
    			else mcmf.ctu();
    
    			if (i == n - 1) printf("%lld
    ", mcmf.cost);
    			else printf("%lld ", mcmf.cost);
    		}
    	}
    	return 0;
    }
    

    1006 The Oculus

    预处理出 (fib[i]\%mod) 的值,且令 (fib[i]\%mod) 的值不同

    已知斐波那契数为 ([1,2e6+5]),所以可以预处理 (mod) 是否每个斐波数的模数不同

    根据斐波那契数编码,将 (a、b、c) 求余 (mod) 的值计算出来出来

    由于 (fib[i]\%mod) 的值各不相同,则存在唯一值使得 ((c+fib[i])\%mod=a*b\%mod)

    比赛时想法不是很准确,然后WA2,只不过最后还是出了,下次还是要思考充分一些。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    const LL mod = 3799912185593857;
    const int maxn = 2e6 + 5;
    LL fib[maxn];
    int a[maxn];
    
    int main() {
    	fib[0] = fib[1] = 1;
    	for (int i = 2; i < maxn; i++) {
    		fib[i] = fib[i - 1] + fib[i - 2];
    		if (fib[i] >= mod) fib[i] -= mod;
    	}
    
    	int t;
    	scanf("%d", &t);
    	while (t--) {
    		int n, x, p, q;
    		scanf("%d", &p);
    		LL a = 0;
    		for (int i = 1; i <= p; i++) {
    			scanf("%d", &x);
    			if (x) {
    				a = a + fib[i];
    				if (a >= mod) a -= mod;
    			}
    		}
    		scanf("%d", &q);
    		LL b = 0;
    		for (int i = 1; i <= q; i++) {
    			scanf("%d", &x);
    			if (x) {
    				b = b + fib[i];
    				if (b >= mod) b -= mod;
    			}
    		}
    		scanf("%d", &n);
    		LL c = 0;
    		for (int i = 1; i <= n; i++) {
    			scanf("%d", &x);
    			if (x) {
    				c = c + fib[i];
    				if (c >= mod) c -= mod;
    			}
    		}
    		LL ans = __int128(a) * b % mod;
    		for (int i = 1; i <= max(n, p + q + 1); i++) {
    			LL temp = c + fib[i];
    			if (temp >= mod) temp -= mod;
    			if (temp == ans) {
    				printf("%d
    ", i);
    				break;
    			}
    		}
    	}
    	return 0;
    }
    

    1007 In Search of Gold

    • 由于求树上最小的最长路径考虑用二分,二分最小直径为 (mid)

    • (dp) 验证最小直径

      (dp[i][j]) 表示以 (i) 为根且有 (j)(a) 边的子树中,所有符合条件的直径上,最长的到 (i) 点的点的距离

      以根节点为 (now) 的树为例,新搜索了一棵根节点为 (v) 的子树

      • (now-v)(a),将已知的 (dp[now][i])(dp[v][j]) 合并
        • 若合并大于 (mid),则 (now-v = a), 有 (i+j+1)(a) 边的树中必定存在直径非法
        • 若合并小于等于 (mid),则取 (max(dp[now][i],dp[v][j]+a)) 为最远的点
      • (now-v)(b)
        • 若合并大于 (mid),则 (now-v = b), 有 (i+j)(a) 边的树中必定存在直径非法
        • 若合并小于等于 (mid),则取 (max(dp[now][i],dp[v][j]+b)) 为最远的点
      • (a,b) 边的两种情况取 (min),为最小的最远的点

      验证 (dp[root][1]) 即可

    比赛时也要类似想法,但并未尝试,主要还是时间不足,加上没有充足信心。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    const int maxn = 2e4 + 5;
    int head[maxn], tot;
    struct Edge {
        int v;
        int next;
        int a;
        int b;
    } edge[maxn << 1];
    inline void AddEdge(int u, int v, int a, int b) {
        edge[++tot].v = v;
        edge[tot].a = a;
        edge[tot].b = b;
        edge[tot].next = head[u];
        head[u] = tot;
    }
    
    int m_size[maxn];
    LL dp[maxn][21];
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n, k;
            scanf("%d%d", &n, &k);
            memset(head, 0, sizeof(int) * (n + 1));
            tot = 0;
            int u, v, a, b;
            for (int i = 1; i < n; i++) {
                scanf("%d%d%d%d", &u, &v, &a, &b);
                AddEdge(u, v, a, b);
                AddEdge(v, u, a, b);
            }
    
            function<void(int now, int fa)> dfs;
            LL left = 0, right = LL(n) * (0X3f3f3f3f), mid, ans = 1e18;
            LL cnt[22];
            dfs = [&](int now, int fa) {
                if (!edge[head[now]].next && fa) {
                    m_size[now] = dp[now][0] = 0;
                    return;
                }
    
                m_size[now] = 0;
                dp[now][0] = 0;
                for (int i = head[now]; i; i = edge[i].next) {
                    int v = edge[i].v;
                    int a = edge[i].a;
                    int b = edge[i].b;
                    if (v == fa)
                        continue;
                    dfs(v, now);
                    memset(cnt, 0X3f, sizeof(cnt));
                    for (int i = 0; i <= m_size[now]; i++) {
                        for (int j = 0; j <= m_size[v] && i + j <= k; j++) {
                            if (dp[now][i] + dp[v][j] + a <= mid)
                                cnt[i + j + 1] = min(cnt[i + j + 1], max(dp[now][i], dp[v][j] + a));
                            if (dp[now][i] + dp[v][j] + b <= mid)
                                cnt[i + j] = min(cnt[i + j], max(dp[now][i], dp[v][j] + b));
                        }
                    }
                    m_size[now] = min(m_size[now] + m_size[v] + 1, k);
                    for (int i = 0; i <= m_size[now]; i++)
                        dp[now][i] = cnt[i];
                };
            };
            while (left <= right) {
                mid = (left + right) >> 1;
                dfs(1, 0);
                if (dp[1][k] <= mid) {
                    right = mid - 1;
                    ans = min(ans, mid);
                } else
                    left = mid + 1;
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    

    1009 It's All Squares

    本题实质是一个算复杂度的问题 。显然最大图形为 (400*400),有 (4e6/4/400=2500) 个图形, (400*400*2500=4e8) 换言之,矩形每个可以用 (O(n*m)) 复杂度完成。

    对于每个多边形处理出 (x_{min},x_{max},y_{min},y_{max}) ,然后每行处理出所有竖的边界,在两个相邻边界内的就是在多边形内

    (对边界排序可以用基数,但是 (sort) 也可以 (qwq))。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 405;
    const int inf = 0X3f3f3f3f;
    int w[maxn][maxn];
    
    char s[maxn * maxn];
    int line[maxn][maxn], top[maxn];
    
    bool a[maxn * maxn];
    int cnt[maxn * maxn];
    
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n, m, q;
            scanf("%d%d%d", &n, &m, &q);
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= m; j++)
                    scanf("%d", &w[i][j]);
    
            for (int l = 1; l <= q; l++) {
                int x, y;
                scanf("%d%d", &x, &y);
                scanf("%s", s + 1);
                int len = strlen(s + 1);
                int min_x = x, max_x = x, min_y = y, max_y = y;
                for (int i = 1; i <= len; i++) {
                    if (s[i] == 'L')
                        x--;
                    else if (s[i] == 'R')
                        x++;
                    else if (s[i] == 'D')
                        y--;
                    else
                        y++;
                    min_x = min(min_x, x);
                    max_x = max(max_x, x);
                    min_y = min(min_y, y);
                    max_y = max(max_y, y);
                    if (s[i] == 'L')
                        line[x + 1][++top[x + 1]] = y;
                    else if (s[i] == 'R')
                        line[x][++top[x]] = y;
                }
    
                int g = 0;
                for (int i = min_x + 1; i <= max_x; i++) {
                    if (!top[i])
                        continue;
                    sort(line[i] + 1, line[i] + top[i] + 1);
    
                    for (int j = 1; j <= top[i]; j += 2) {
                        for (int k = line[i][j] + 1; k <= line[i][j + 1]; k++) {
    
                            if (!a[w[i][k]]) {
                                a[w[i][k]] = true;
                                cnt[++g] = w[i][k];
                            }
                        }
                    }
                    top[i] = 0;
                }
                for (int i = 1; i <= g; i++)
                    a[cnt[i]] = false;
                printf("%d
    ", g);
            }
        }
        return 0;
    }
    

    1010 Lead of Wisdom

    (n) 个装备 (k)((n,kleq 50)) ,所以装备方案最多为 (3^{16}*2),所以直接爆搜+剪枝。

    剪枝可以剪下界,(事实上,不剪也可以过)用每种装备最大的 (a,b,c,d) 代表每种装备的最优取值,用后缀表示最优装备的后缀,在搜索过程中若采用最优后缀仍无法超过已知最优解,则可以剪枝。

    前期剪枝出了一些问题,然后debug花了比较多时间,期间WA4。总体还是因为签到没过导致压力大,过于焦急。

    下次debug时要注意冷静,否则会事倍功半。

    #include<bits/stdc++.h>
    #define ll long long
    #define maxn 100010
    using namespace std;
    ll ma[100][5];
    ll sum[100][5];
    ll val[100][5];
    ll ans = 0;
    int n, k;
    vector<int>b[100];
    void sol(int x, ll a1, ll a2, ll a3, ll a4) {
    	ans = max(ans, (a1 + 100) * (a2 + 100) * (a3 + 100) * (a4 + 100));
    	if (x == k) return;
    	if (ans >= (a1 + sum[x][0] + 100) * (a2 + sum[x][1] + 100) * (a3 + sum[x][2] + 100) * (a4 + sum[x][3] + 100)) return;
    	for (int i = 0; i < b[x + 1].size(); i++) {
    		int j = b[x + 1][i];
    		sol(x + 1, a1 + val[j][0], a2 + val[j][1], a3 + val[j][2], a4 + val[j][3]);
    	}
    	sol(x + 1, a1, a2, a3, a4);
    }
    
    int main() {
    	int t;
    	scanf("%d", &t);
    	while (t--) {
    		scanf("%d%d", &n, &k);
    		memset(sum, 0, sizeof(sum));
    		memset(val, 0, sizeof(val));
    		memset(ma, 0, sizeof(ma));
    		for (int i = 0; i <= k; i++) b[i].clear();
    		for (int i = 0; i < n; i++) {
    			int x;
    			scanf("%d", &x);
    			for (int j = 0; j < 4; j++) {
    				scanf("%lld", &val[i][j]);
    				ma[x][j] = max(ma[x][j], val[i][j]);
    			}
    			b[x].push_back(i);
    		}
    		for (int i = k - 1; i >= 0; i--)
    			for (int j = 0; j < 4; j++) 
    				sum[i][j] = sum[i + 1][j] + ma[i + 1][j];
    		ans = 0;
    		sol(0, 0, 0, 0, 0);
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    1012 String Distance

    由于 (m) 串只有 (20) ,首先预处理 (n) 串每个字符后面第 (i) 个字符的位置。

    对于每次询问搜索 (LCS) ,用 (dp[i][j]) 表示 (LCS)(i) 位是 (m) 串第 (j) 位时,对应字符在 (n) 串的位置,若在([left,right]) 范围内,即合理。

    [egin{gather*} dp[i+1][j]=min(w[dp[i][j-k]][p[k]-'a'])(1leq k <j) end{gather*} ]

    比赛时稍微出现小问题WA1,发现问题后就解决了。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 5;
    const int inf = 0X3f3f3f3f;
    char s[maxn], p[30];
    int w[maxn][26];
    int dp[21][21];
    
    int main() {
    	int t;
    	scanf("%d", &t);
    	while (t--) {
    		scanf("%s%s", s + 1, p + 1);
    		int n = strlen(s + 1);
    		int m = strlen(p + 1);
    		for (int i = 0; i < 26; i++)
    			w[n][i] = inf;
    		for (int i = n - 1; i >= 0; i--) {
    			for (int j = 0; j < 26; j++)
    				w[i][j] = w[i + 1][j];
    			w[i][s[i + 1] - 'a'] = i + 1;
    		}
    		int q;
    		scanf("%d", &q);
    		while (q--) {
    			int left, right;
    			scanf("%d%d", &left, &right);
    			int ans = 0;
    			memset(dp, 0X3f, sizeof(dp));
    			for (int i = 1; i <= m; i++)
    				dp[1][i] = w[left - 1][p[i] - 'a'];
    			for (int i = 1; i <= m; i++) {
    				for (int j = 1; j <= m; j++) {
    					if (dp[i][j] <= right) {
    						ans = max(ans, i);
    						for (int k = j + 1; k <= m; k++)
    							dp[i + 1][k] = min(dp[i + 1][k], w[dp[i][j]][p[k] - 'a']);
    					}
    				}
    			}
    			ans = right - left + 1 - ans + m - ans;
    			printf("%d
    ", ans);
    
    		}
    	}
    	return 0;
    }
    

    这场总体表现比上一场要好一些,部分问题有所改善,但前期被卡还是难顶。

    每场比赛都会反映队伍总体的状态,希望我们能在比赛中调整好心态,不有太大的压力。

  • 相关阅读:
    java web项目打包.war格式
    version 1.4.2-04 of the jvm is not suitable for thi
    Sugarcrm Email Integration
    sharepoint 2010 masterpage中必须的Content PlaceHolder
    微信开放平台
    Plan for caching and performance in SharePoint Server 2013
    使用自定义任务审批字段创建 SharePoint 顺序工作流
    Technical diagrams for SharePoint 2013
    To get TaskID's Integer ID value from the GUID in SharePoint workflow
    how to get sharepoint lookup value
  • 原文地址:https://www.cnblogs.com/st1vdy/p/13368701.html
Copyright © 2011-2022 走看看