2017 Multi-University Training Contest
来源:互联网 发布:2016乒超联赛网络 编辑:程序博客网 时间:2024/06/13 23:45
HDU - 6045- Is Derek lying?(思维题)
题意:
n个数,给出A的分数,B的分数,对一题得一分,然后给出每个人每道题的选择,每个题目有ABC三个选项,问是否存在这种可能
思路:
找出两个人的选择不同的数量diff。然后计算两个人的得分之差。如果能用diff去弥补,那么就存在这种可能性。反之则不行。
然后还要判断一下,两者分数是否过高。画个图,“尽量错”使得分数总和有可能达到,也是通过diff去考虑。
#include <bits/stdc++.h>using namespace std;const int maxn = 80000 + 5;char a[maxn];char b[maxn];int main(){ int T, n, x, y; scanf("%d", &T); while(~scanf("%d%d%d", &n, &x, &y)) { scanf("%s", a); scanf("%s", b); int diff = 0; for(int i = 0; i < n; i++) { if(a[i] != b[i]) diff++; } int delta = abs(x - y); if(delta <= diff && 2 * n - diff >= x + y) puts("Not lying"); else puts("Lying"); } return 0;}
HDU - 6047 - Maximum Sequence(贪心)
题意:
给定数组a和数组b的前n项,让你根据以下公式得到补全数组a[n + 1] - a[2n]。
从b数组中取出一个元素bk,使得的值最大。
思路:
实际上每次就是查询a[bk] - a[i - 1]之间{a[x] - x}的最大值,然后填到a[i]里。然后显然我们需要把b数组从小到大排序,来赋值比较好。这样贪心很显然。
然后我们发现,a[n + 1] - a[i - 1]这有一右半段区间的数每一次都是要查询的,而且他是单调非递增的。思考一下为什么。因为你取最大值的区间变小了,那么最大值一定是不会超过原来的最大值的,而且你减去的东西也变大了。甚至是一个单调递减的吧。所以这一块的最大值应该就是a[n + 1]。
那左半区间a[bk] - a[n]的最大值怎么查呢,我们可是要查250000次的呢。这里我们可以做到O(1)的查询。用什么呢?
你想啊这左半区间的查询最大值,是不是一个右端点固定左端点不断增大的过程。那么从右边a[n]开始维护一个最大值的数组left,是不是可以做到left[i] : 是从a[i] - a[n]的最大值。left[i] = max(a[i], left[i + 1])。是不是还算巧妙的方法。
#include <bits/stdc++.h>using namespace std;const int maxn = 500000 +5;const int mod = 1e9 + 7;int a[maxn];int b[maxn];int l[maxn];int r[maxn];int main(){ int n; while(~scanf("%d", &n)) { for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int j = 1; j <=n; j++) scanf("%d", &b[j]); l[n] = a[n] - n; for(int i = n - 1; i >= 1; i --) { l[i] = max(l[i + 1], a[i] - i); } sort(b + 1, b + n); long long ans = 0; for(int i = 1; i <= n; i++) { int idx = b[i], val; if(i == 1) { val = l[idx]; a[n + i] = val; r[i + n] = val - n- i; } else { val = max(l[idx], a[n + 1] - n - 1); a[n + i] = val; } ans = ans +val; ans %= mod; } printf("%lld\n", ans); } return 0;}
HDU - 6050 - Funny Function(数学公式推导)
题意:
给定N,M。(1e18)。求F(M,1)。
思路:
公式推导,利用数列知识推得F(1, i)的通项公式,然后可以尝试的写一下F(2,j),会发现可以利用等比数列求和公式算出答案,同理F(3,j)。就会发现规律了。这题说的很简略,有困惑的可以留言交流。
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 1e9 + 7;//求余版本LL quickPow(LL a, LL b, LL Mod){ LL ret = 1; while(b) { if(b & 1) ret = ret * a % Mod; b >>= 1; a = (a * a) % Mod; } return ret;}int main(){ int T; scanf("%d", &T); while(T--) { LL n, m; scanf("%lld%lld", &n, &m); LL ans; LL inv3 = quickPow(3, mod - 2, mod); LL temp = quickPow(2, n, mod); temp --; if(n & 1) { ans = (2LL * quickPow(temp, m - 1, mod) + 1) % mod * inv3 % mod; } else { ans = 2LL * quickPow(temp, m - 1, mod) % mod * inv3 % mod; } printf("%lld\n", ans); } return 0;}
HDU - 6052 - To my boyfriend(计数问题)
题意:
给出一个N*M的数字矩阵。求子矩阵的不同数字的个数的期望。(限制 N,M <= 100)
思路:
枚举每个格子对答案的贡献度。我们若想要不重复的计数,我们要怎么办呢。
规定:每个矩形当中同一种颜色,只会对答案贡献1,那么规定矩形中同色的,只有自左向右,自上向下的第一个对这个矩形有贡献。
那么假设我们枚举到点(x,y),我们枚举高度h(x >= h >= 1),然后向左向右探测,找到左右边界lb,rb。此时。此时(x,y)对(y - lb + 1) * (rb - y + 1)*(n - x + 1)个矩形有贡献。怎么理解呢。枚举了一个高度h的时候在第h行上,位于点(x,y)左边只有一排(y - lb + 1)个点可以作为矩形的左上顶点,(rb - y + 1)*(n - x + 1)个点可以作为右下顶点。这样就能算出来包含(x,y)的矩形有几个,即这个点对答案的贡献是多少。那么我们每次calc一个点(x,y)的复杂度是枚举高度*枚举左右边界,O(n^2)。又要进行O(n^2)次calc。那么总的复杂度是O(n^4)。
但是呢我们可以继续加速这个过程。做到O(n^3),有兴趣的同学可以搜一下别的题解。
#include <bits/stdc++.h>using namespace std;typedef long long LL;int ma[105][105];int n, m;LL solve(int x, int y){ LL ret = 0; int color = ma[x][y]; int lb = 1, rb = m; for(int i = x; i >= 1; i--) { if(i != x && ma[i][y] == color) break; int l = y, r = y; for(int j = y - 1; j >= max(1, lb); j--) { if(ma[i][j] == color) break; l = j; } lb = max(l, lb); if(i == x) { ret += 1LL * (y - lb + 1) * (n - x + 1) * (rb - y + 1); continue; } for(int j = y + 1; j <= min(m, rb); j++) { if(ma[i][j] == color) break; r = j; } rb = min(r, rb); ret += 1LL * (y - lb + 1) * (n - x + 1) * (rb - y + 1); } return ret;}int main(){ int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { scanf("%d", &ma[i][j]); } } LL ans = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { ans += solve(i, j); } } LL tot = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { tot += 1LL * i * j; } } printf("%.9f\n", 1.0 * ans / tot); } return 0;}
HDU - 6053- TrickGCD(容斥 莫比乌斯反演)
题意:
给你n个数字,b数组每个位置i的数字b[i]可以小于等于a[i],求所有区间(l,r)都满足gcd(l,r)大于等于2的情况数的b数组的方案数。
思路:
这题有两种做法,莫比乌斯反演的做法欢迎留言交流了,因为都是数学公式,写出来比较麻烦。如果枚举b数组整体gcd的结果为x,那么显然对于x有种可能。但是这个时候会有重复计数的问题。我们可以发现这个时候,恰好容斥所需要的系数是莫比乌斯函数的相反数,所以乘上这个系数就能做到容斥了。可是复杂度还是一个O(n^2)的级别呢。我们采取分块+快速幂的策略。利用的是怎么个思想呢,对于[kg,(k+1)g)区间的数字而言,/g的结果均为k,所以我们可以变成枚举b数组整体gcd的结果i,for(int i = 2; i <= minn; i++)。
然后查询他的每个倍数j,for(int j = i; j <= maxx; j += i)。利用前缀和预处理以后可以在O(1)的时间内得到整个区间/g为1的数字的数量,为2的数字的数量。然后通过快速幂计算出来。这个地方把n^2的复杂度优化到了nlognlogn的级别。
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 1e9 + 7;const int maxn = 1e5 + 5;int a[maxn];int pre[maxn];//线性筛法求莫比乌斯函数bool check[maxn];int prime[maxn];int mu[maxn];void Moblus(){ memset(check,false,sizeof(check)); mu[1] = 1; int tot = 0; for(int i = 2; i < maxn; i++) { if( !check[i] ){ prime[tot++] = i; mu[i] = -1; } for(int j = 0; j < tot; j++) { if(i * prime[j] > maxn) break; check[i * prime[j]] = true; if( i % prime[j] == 0){ mu[i * prime[j]] = 0; break; }else{ mu[i * prime[j]] = -mu[i]; } } }}LL quickPow(LL a, LL b, LL Mod){ LL ret = 1; while(b) { if(b & 1) ret = ret * a % Mod; b >>= 1; a = (a * a) % Mod; } return ret;}int main(){ Moblus(); int T; scanf("%d", &T); int cas = 1; while(T--) { memset(pre, 0, sizeof(pre)); int n; scanf("%d", &n); int minn = 0x3f3f3f3f; int maxx = -1; for(int i = 0; i <n; i++) { scanf("%d", &a[i]); minn = min(minn, a[i]); maxx = max(maxx, a[i]); pre[a[i]]++; } long long ans = 0; for(int i = 1; i < maxn; i++) pre[i] += pre[i - 1]; for(int i = 2; i <= minn; i++) { int cnt = 1; LL temp = 1; if(mu[i] == 0) continue; for(int j = i; j <= maxx; j += i) { int r = min(maxx, j + i - 1); int l = j; temp = temp * quickPow(cnt, pre[r] - pre[l - 1], mod) % mod; cnt ++; } temp = (temp * -mu[i] + mod) % mod; ans = (ans + temp) % mod; } printf("Case #%d: %lld\n", cas++, ans); } return 0;}
HDU - 6055 - Regular polygon(结论题)
题意:
给你n个点的坐标,坐标以整型输入,问所有的点能组成多少个正多边形。(n<=500)
思路:
有一个结论:整点组成的正多边形只能是正四边形。
那么我们需要知道n个点能组成多少个正四边形。我们直接枚举两个点作为正方形的邻边,check就行了。这题是暴力枚举。至于,由两个点枚举的邻边推出剩下两个点的坐标呢,我们知道一个正方形可以补四个全等三角形组成一个大正方形吧。当前正方形外面再套一个大的正方形,大正方形的边与坐标轴平行,然后就在x,y方向上挪动一定量就能得到另外两个点了,画一下图就懂了,初中几何问题。
注意!!!check的时候,用的是vis[x][y]的check这个(x,y)这个整点是否输入了的时候,一定要注意,检查x,y是否越界了!!!。
#include <bits/stdc++.h>using namespace std;#define x first#define y secondconst int maxn = 500 + 5;pair<int, int>nodes[maxn];int vis[maxn][maxn];int main(){ int n; while(~scanf("%d", &n)) { memset(vis, 0, sizeof(vis)); for(int i = 0; i < n; i++) { int x, y; scanf("%d%d", &x, &y); x += 100, y += 100; nodes[i] = {x, y}; vis[x][y] = 1; } int ans = 0; for(int i = 0; i < n; i++) { for(int j = i + 1; j < n; j++) { pair<int, int>a = nodes[i], b = nodes[j]; int n = a.x - b.x; int m = a.y - b.y; if(0 <= a.x - m && 0 <= a.y + n && 0 <= b.x - m && 0 <= b.y + n && vis[a.x - m][a.y + n] && vis[b.x - m][b.y + n]) ans++; if(0 <= a.x + m && 0 <= a.y - n && 0 <= b.x + m && 0 <= b.y - n && vis[a.x + m][a.y - n] && vis[b.x + m][b.y - n]) ans++; } } printf("%d\n", ans / 4); } 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
- JAVASE基础(五)
- 滑动让物体旋转
- 获取ul下li标签里点击的是哪一个li并获取li里a标签的值
- root密码忘记
- 守卫问题The Festive Evening
- 2017 Multi-University Training Contest
- idea——发布springmvc报404
- Linux下./configure参数详解
- 使用FloatingActionButton
- hdu 6058 Kanade's sum [区间第k大数求和] [2017 Multi-University Training Contest
- 数据结构-静态链表及其插入删除操作
- 【HDU2012】素数判定
- 目前的定时调度都用Spring Schedule 任务调度实现
- python AttributeError: module 'string' has no attribute 'uppercase'