Codeforces Round #393 (Div. 2) (8VC Venture Cup 2017 - Final Round Div. 2 Edition)
传送门:http://codeforces.com/contest/760
A题:给你非闰年的某个月份m,它在日历上是从第d行开始排列的,问这个月要占多少列。直接算一下就行了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5 + 10; const int INF = 0x3f3f3f3f; const int N[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int main() { int n, m; cin >> n >> m; int all = N[n] + m - 1; cout << (all + 6) / 7; return 0; }
B题:有n个人在同一排睡觉,一共有m个枕头,Frodo在第k位。枕头的分配规则是每个人至少要有一个枕头且相邻的人的枕头数量差最多为1。问Frodo最多能拿到多少个枕头。因为每个人至少要拿一个枕头,所以先m-=n,最后答案再加1。再考虑贪心,如果Frodo要拿到更多的枕头,那他周围的人一定是比他少一个枕头,那么每个人获得的枕头数是由1递增到k,再由k递减到n,公差为1。假设Frodo获得了ak个枕头,直接由等差公式可以算出a1=ak-k+1,an=ak-(n-k),要注意a1和an可能是负数,所以计算的时候还要处理一下。根据等差数列求和公式可以算出总枕头数,因此这题直接二分ak,找最接近m的答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5 + 10; const int INF = 0x3f3f3f3f; ll n, m, k; ll f(ll ak) { ll a1 = ak - k + 1, an = ak - (n - k); ll L = (a1 + ak) * k / 2, R = (ak + an) * (n - k + 1) / 2 - ak; if (a1 <= 0) L = (1 + ak) * ak / 2; if (an <= 0) R = (1 + ak) * ak / 2 - ak; return L + R; } ll solve() { ll L = 0, R = m; while (L <= R) { ll mid = (L + R) >> 1; ll ans = f(mid); if (ans > m) R = mid - 1; else L = mid + 1; } return R + 1; } int main() { cin >> n >> m >> k; m -= n; cout << solve() << endl; return 0; }
C题:题意大概是Pavel要n个地点烤肉,给你个数组p,p[i]代表可以从第i个位置到第p[i]个位置,p数组为n的一个全排列,又有个数组b,b[i]为1的时候,当Pavel走到第i个位置必须把这里的烤肉翻面。Pavel可以改变p数组或者b数组上的任意一个元素。问Pavel至少要改变多少个元素使得他可以把所有烤肉正反两面都烤一次。这题其实就是要先把路径改成一个环,因为只有成了环,Pavel才能走完两圈。由于p是全排列,因此这个路径本身就是一个或者多个环组合而成的。我们考虑n个环之间要合并,需要改动n条边,所以可以并查集或者dfs出环(联通块)的数量,这就是p数组需要改变的元素个数。然后我们考虑,假设最后合并成的环上,如果有奇数个1,Pavel就可以正反都烤到肉,如果是偶数个1就不行,因此判断下1的个数的奇偶性,就能得出b数组要改变多少次。需要特判环数为1的时候,p数组不需要改变。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 2e5 + 10; const int INF = 0x3f3f3f3f; int f[maxn], sum; void init(int n) { for (int i = 0; i <= n; i++) f[i] = i; } int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); } void mix(int x, int y) { int fx = find(x), fy = find(y); if (fx == fy) return ; f[fx] = fy; sum--; } int main() { int n, x, b = 0; scanf("%d", &n); init(n); sum = n; for (int i = 1; i <= n; i++) { scanf("%d", &x); mix(i, x); } for (int i = 1; i <= n; i++) { scanf("%d", &x); if (x) b++; } if (sum == 1) sum = 0; cout << sum + (!(b & 1)) << endl; return 0; }
D题:有三种车票,一种能坐一个站,花费20。第二种可以坐90-1(这里-1是因为题目说在站台要花费1分钟,所以坐的时间-1)分钟,花费50。第三种可以坐1440-1分钟,花费120。现在有n个站,坐到第i个站需要时间ti,ti是递增的。从开始坐到每一个站,都要用最优的方式买票,现在问做到第i个站,需要多交多少钱。设dp[i]为到达第i站的最小花费,如果选第一种票,dp[i]=dp[i-1]+20;如果考虑第二种票,我们可以二分求出ti-89在第pos个站,然后比较(i-pos+1)和50哪个大,选一个最小的,第三种票同理。输出dp[i]-dp[i-1]即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5 + 10; const int INF = 0x3f3f3f3f; ll N[maxn], dp[maxn]; int main() { int n; cin >> n; for (int i = 1; i <= n; i++) cin >> N[i]; for (int i = 1; i <= n; i++) { dp[i] = dp[i-1] + 20; ll num1 = N[i] - 89; ll num2 = N[i] - 1439; // if (num1>0) { ll pos = lower_bound(N + 1, N + i, num1) - (N + 1); ll w = (i - pos + 1) * 20; if (w > 50) dp[i] = min(dp[i], dp[pos] + 50); else dp[i] = min(dp[i], dp[pos] + w); } // if (num2>0) { ll pos = lower_bound(N + 1, N + i, num2) - (N + 1); ll w = (i - pos + 1) * 20; if (w > 120) dp[i] = min(dp[i], dp[pos] + 120); else dp[i] = min(dp[i], dp[pos] + w); } cout << dp[i] - dp[i-1] << endl; } return 0; }
总结:这场打得好惨,熬夜打码本来就没什么精神,b题推公式写错一点,debug了半天,c和d又看不懂,最后两题收尾,上场打的蓝名又没有了= =绝望。感觉必须恶补下英语了。