2017 Multi-University Training Contest
来源:互联网 发布:图书借阅管理系统 php 编辑:程序博客网 时间:2024/06/11 22:34
HDU - 6069- Counting Divisors(区间筛)
题意:
求。l和r为1e12,但是,r-l不到1e6。d函数为因子个数。
思路:
一个数的因子个数可以由它的素因子个数推出, ,那么因子个数为。可以理解成第一个素数一共有0-x1,一共x1+1种选择。
那么现在就是要知道[l,r]区间内,所有数字的素因子。而我们又知道,"l和r为1e12,r-l不到1e6"这个条件,在《挑战》的2.6的数学中有介绍一个东西叫做区间筛,可以在nlogn的复杂度内求出[l,r]区间内的素数。我们可以借鉴它的思想,先求出[1,sqrt(r)]区间内的素数,然后在不断删除[l,r]中它的倍数,每个v[i]在经过[1,sqrt(r)]区间内的素数筛选后,剩下的值,若不等于1,则必为素数。(这个好理解的吧?类似求一个数x的素因子的时候,只需要遍历sqrt(x)内的素数),那么就能知道每个数字不同的素因子的个数了。累乘最后加和即可。
埃氏筛,数字大的时候,for循环里要加LL!!!不然j = i * i会爆。
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e6 + 5;const int mod = 998244353;LL v[maxn];int notprime[maxn];//vector<int>prime;LL num[maxn];int prime[maxn];int sieve(LL mx){ int ret = 0; for(int i = 2; i <= mx; i++) { if(!notprime[i]) { prime[ret++] = i; if(1LL * i * i > mx) continue; for(int j = i * i; j <= mx; j += i) { notprime[j] = 1; } } } return ret;}int main(){ int p = sieve(maxn - 5); int T; scanf("%d", &T); while(T--) { LL l, r; int k; scanf("%lld%lld%d", &l, &r, &k); for(int i = 0; i <= r - l; i++) num[i] = 1, v[i] = l + i; int mx = sqrt(r); for(int j = 0; j < p; j++) { int o = prime[j]; if(o > mx) break; for(LL i = (l + o - 1) / o * o; i <= r; i += o) { LL idx = i - l, cnt = 0; while(v[idx] % o == 0) { v[idx] /= o; cnt++; } if(cnt) num[idx] = num[idx] * (1LL * cnt * k + 1) % mod; } } long long ans = 0; for(LL i = 0; i <= r - l; i++) { LL temp = (v[i] == 1) ? 1 : k + 1; num[i] = num[i] * temp % mod; ans = (ans + num[i]) % mod; } printf("%lld\n", ans); } return 0;}
HDU - 6070 - Dirt Ratio(二分+线段树)
题意:
题意大概就是能否找到一区间,使得 区间内不同的数字的个数 / 区间长度 的值 最小。
思路:
区间最优比率问题,往往是需要二分答案来解决的。如果我们假定答案为mid,那么很容易由题意得到。,即得到size(l,r)代表[l,r]区间内不同数字的数量。我们发现我们需要快速的找到[1,n]之间这个值,最小的区间的值是否满足小于等于0。那么需要维护一个区间最小值,所以要用线段树。所以是二分+线段树。
具体的操作呢,我们枚举区间右端点,想一下每个区间维护的值由两部分组成,第一部分是size。考虑到每个数字只对[自己上一次出现的位置+1,当前位置]的各个区间有贡献度1。只需要枚举右端点时候,对这部分+1即可。第二部分是长度*mid,只需要对枚举到的位置中的每个区间-mid即可。
#include <bits/stdc++.h>using namespace std;#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1 | 1const int maxn = 60000 + 6;int a[maxn], last[maxn], pre[maxn];double lazy[maxn << 2];double tree[maxn << 2];int T, n;void pushup(int rt){tree[rt] = min(tree[rt << 1], tree[rt << 1 | 1]);}void pushdown(int rt){ if(lazy[rt]) { lazy[rt << 1] += lazy[rt]; lazy[rt << 1 | 1] += lazy[rt]; tree[rt << 1] += lazy[rt]; tree[rt << 1 | 1] += lazy[rt]; lazy[rt] = 0; }}void build(int l, int r, int rt){ lazy[rt] = tree[rt] = 0; if(l == r) return ; int mid = (l + r) / 2; build(lson); build(rson); pushup(rt);}void update(int ql, int qr, double delta, int l, int r, int rt){ if(ql <= l && r <= qr) { tree[rt] += delta; lazy[rt] += delta; return ; } pushdown(rt); int mid = (l + r) / 2; if(ql <= mid) update(ql, qr, delta, lson); if(qr > mid) update(ql, qr, delta, rson); pushup(rt);}double query(int ql, int qr, int l, int r, int rt){ if(ql <= l && r <= qr) return tree[rt]; pushdown(rt); int mid = (l + r) / 2; double ret = n; if(ql <= mid) ret = min(ret, query(ql, qr, lson)); if(qr > mid) ret = min(ret, query(ql, qr, rson)); pushup(rt); return ret;}bool judge(double mid){ build(1, n, 1); for(int i = 1; i <= n; i++) { update(pre[i] + 1, i, 1.0, 1, n, 1); update(1, i, -mid, 1, n, 1); if(query(1, i, 1, n, 1) <= 0.0) return true; } return false;}int main(){ scanf("%d", &T); while(T--) { scanf("%d", &n); for(int i = 1; i <= n; i++) last[i] = pre[i] = 0; for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); pre[i] = last[a[i]]; last[a[i]] = i; } double lb = 0.0, rb = 1.0; for(int i = 0; i < 20; i++) { double mid = (lb + rb) / 2; if(judge(mid)) rb = mid; else lb = mid; } printf("%.6f\n", rb); } return 0;}
HDU - 6071 - Lazy Running(同余最短路)
题意:
题目要求是只有四个点,然后连边成正方形,问从2号点出发,再回到2号点且走过的总距离大于K的最短的路径是多少。
思路:
我们可以通过,构造同余最短路。每当找到一个长度为k的从某点(1,2,3,4)出发到达点2的最短路时,一定会存在一个2m+k的答案。所以只要计算出各个点到点2的,%2m为[0, k - 1],的最短路即可。
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 60000 + 6;const int INF = 0x3f3f3f3f;typedef pair<int, long long>pil;typedef pair<int, int>pii;vector<pii>G[10];LL d[5][maxn];int vis[5][maxn];LL spfa(int st, LL mod, LL k){ queue<pil> que; memset(vis, 0, sizeof(vis)); memset(d, 0x3f, sizeof(d)); que.push({st, 0}); d[st][0] = 0; vis[st][0] = 1; LL ans = 1e18 + 5000; while(que.size()) { int u = que.front().first; LL dist = que.front().second; que.pop(); vis[u][dist % mod] = 0; for(auto o : G[u]) { int v = o.first, cost = o.second; LL next_dis = dist + cost; if(v == st) { if(next_dis < k) ans = min(ans, next_dis + (k - next_dis + mod - 1) / mod * mod); else ans = min(ans, next_dis); } if(next_dis < d[v][next_dis % mod]) { d[v][next_dis % mod] = next_dis; if(vis[v][next_dis % mod] == 0) { vis[v][next_dis % mod] = 1; que.push({v, next_dis}); } } } } return ans;}int main(){ int T_T; scanf("%d", &T_T); while(T_T--) { LL k; int d12, d23, d34, d41; scanf("%lld%d%d%d%d", &k, &d12, &d23, &d34, &d41); for(int i = 0; i <= 5; i++) G[i].clear(); G[1].push_back({2, d12}); G[1].push_back({4, d41}); G[2].push_back({1, d12}); G[2].push_back({3, d23}); G[3].push_back({2, d23}); G[3].push_back({4, d34}); G[4].push_back({1, d41}); G[4].push_back({3, d34}); int m = min(d12, d23); m = m * 2; printf("%lld\n", spfa(2, m, k)); } return 0;}
HDU - 6073 - Matching In Multiplication(拓扑+贡献度)
题意:
T组测试数据,给出两个顶点集合,左边为U,右边为V,每个顶点集合的大小为n,然后有n行输入,第i行的输入v1,w1,v2,w2,表示集合U中的i顶点与集合V中的v1,v2顶点相连,且边的权值为w1,w2。求两个集合的所有完美匹配的权值之和。一个完美匹配的权值为该匹配所有边的权值相乘,且数据保证至少存在一个完美匹配。
思路:
这篇博客讲的很好了。http://blog.csdn.net/mr__kid/article/details/76684660
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 998244353;const int maxn = 300000 + 5;typedef pair<int, int>pii;int d[maxn * 2];int vis[maxn * 2];int used[maxn * 2];vector<pii>G[maxn * 2];LL ans;void addEdge(int u, int v, int w){ G[u].push_back({v, w}); G[v].push_back({u, w}); d[u]++, d[v]++;}void Topological(int n){ memset(vis, 0, sizeof(vis)); queue<int>que; for(int i = n + 1; i <= 2 * n; i++) if(d[i] == 1) que.push(i); while(que.size()) { int u = que.front();que.pop(); vis[u] = 1;// cout << "u = " << u <<endl; if(d[u] > 1) continue; for(auto o : G[u]) { int v = o.first, w = o.second; if(vis[v]) continue; d[v]--; if(d[v] == 1) { if(u > n) ans = ans * w % mod; que.push(v); } } }}LL part[2];void dfs(int u, int fa, int lv, int eg){ vis[u]++; for(auto o : G[u]) if(o.first != fa) { int v = o.first, w = o.second; if(v == eg) part[lv] = part[lv] * w % mod; if(vis[v]) continue; part[lv] = part[lv] * w % mod; dfs(v, u, 1 - lv, eg); }}int main(){ int T; scanf("%d", &T); while(T--) { int n; scanf("%d", &n); for(int i = 1; i <= 2 * n; i++) G[i].clear(), d[i] = 0; for(int i = 1; i <= n; i++) { int v1, w1, v2, w2; scanf("%d%d%d%d", &v1, &w1, &v2, &w2); addEdge(i, v1 + n, w1); addEdge(i, v2 + n, w2); } ans = 1; Topological(n);// cout << ans << endl; for(int i = 1; i <= n; i++) { if(vis[i] == 0) { part[0] = part[1] = 1; dfs(i, -1, 1, i); ans = ans * (part[0] + part[1]) %mod;// printf("part1 = %lld, part2 = %lld\n", part[0], part[1]); } } printf("%lld\n", ans); } return 0;}/*10054 88 2 831 45 3 855 45 1 523 15 5 762 62 3 33*/
HDU - 6075 - Questionnaire(water)
题意:
给出n个数字,%m==k的数字数量 是否 不小于 其他数字的数量 的判断结果。构造出一个m和k。
思路:
水题。%2的结果必然满足。
#include <bits/stdc++.h>using namespace std;int main(){ int T; scanf("%d", &T); while(T--) { int n; scanf("%d" ,&n); int cnt = 0; for(int i = 0; i < n; i++) { int x; scanf("%d", &x); cnt += (x & 1); } if(cnt >= n - cnt) { printf("2 1\n"); } else { printf("2 0\n"); } } return 0;}
HDU - 6077 - Time To Get Up(water)
模拟即可。
#include <bits/stdc++.h>using namespace std;char s[50][50];bool judge(int x, int y){return s[x][y] == 'X';}int solve(int i, int j){ int a = judge(i, j + 1); int b = judge(i + 1, j + 3); int c = judge(i + 4, j + 3); int d = judge(i + 6, j + 1); int e = judge(i + 4, j); int f = judge(i + 1, j); int g = judge(i + 3, j + 1); int sum = a + b + c +d + e + f + g; if(sum == 2)return 1; else if(sum == 5) { if(c == 0 && f == 0) return 2; else if(c == 1 && f == 0) return 3; else if(c == 1 && f == 1) return 5; } else if(sum == 3) return 7; else if(sum == 4) return 4; else if(sum == 7) return 8; else if(g == 0) return 0; else if(b == 0) return 6; else if(e == 0) return 9;}int main(){ int T; scanf("%d", &T); while(T--) { for(int i = 0; i < 7; i++) { scanf("%s", s[i]); } int x1 = solve(0, 0); int x2 = solve(0, 5); int x3 = solve(0, 12); int x4 = solve(0, 17); printf("%d%d:%d%d\n", x1, x2, x3, x4); } return 0;}
HDU - 6078 - Wavel Sequence(DP)
题意:
想在有两个序列a,b。从两个序列中挑出相同数量的数字,按照原本的顺序。求满足以下条件的子序列的种数。
1. 两个序列相同
2.满足如下形式: ,的子序列的种数。
思路:
首先是一个类LCS的东西。定义dp[i][j][k]为a串前i个且以b串j结尾且上升状态为k时的方案数。
#include <bits/stdc++.h>using namespace std;const int mod = 998244353;const int maxn = 2000 + 5;int a[maxn], b[maxn];long long dp[maxn][maxn][2];void fun(long long &a, long long b){ a = (a + b) % mod;}int main(){ int T; scanf("%d", &T); while(T--) { int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= m; i++) scanf("%d", &b[i]); memset(dp, 0, sizeof(dp)); long long ans = 0; for(int i = 1; i <= n; i++) { long long sum0 = 0, sum1 = 1; for(int j = 1; j <= m; j++) { fun(dp[i][j][0], dp[i - 1][j][0]), fun(dp[i][j][1], dp[i - 1][j][1]); if(a[i] == b[j]) { fun(dp[i][j][0], sum1); fun(dp[i][j][1], sum0); } if(a[i] < b[j]) { fun(sum1, dp[i - 1][j][1]); } else if(a[i] > b[j]) { fun(sum0, dp[i - 1][j][0]); } } } for(int i = 1; i <= m; i++) fun(ans, dp[n][i][0]), fun(ans, dp[n][i][1]); printf("%lld\n", ans); } return 0;}
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- #2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- #2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- [SDUT](3468)广度优先搜索练习之神奇的电梯 ---BFS(图)
- poj-2387 Til the Cows Come Home(最短路-spfa)
- mongo 添加索引(单字段索引, 联合索引 and union 区别)
- 卸载centos7自带的OpenJDK
- bzoj2241[SDOI2011]打地鼠
- 2017 Multi-University Training Contest
- ZooKeeper快速入门
- open,write,read与fopen,fwrite,fread的区别
- 安卓开发学习资源汇总
- DBVisualizer 解决中文乱码问题
- 总结一下基础和我遇到的问题 (关键字 -- 数据类型)
- 代码优化片段--多处使用同一个对象时
- LeetCode 98 Validate Binary Search Tree(Python详解及实现)
- centos7自学之2-jdk配置