模拟赛小结:The 2019 China Collegiate Programming Contest Harbin Site
比赛链接:传送门
上半场5题,下半场疯狂挂机,然后又是差一题金,万年银首也太难受了。
(每次银首都会想起前队友的灵魂拷问:你们队练习的时候进金区的次数多不多啊?)
Problem J. Justifying the Conjecture 00:09 (+) Solved by Dancepted
签到。好像见过很多次了,经典水题。然后英语太差了,理解题意用了不少时间。
代码:
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int x;
scanf("%d", &x);
if (x <= 5) {
puts("-1");
continue;
}
else if (x&1) {
cout << 3 << ' ' << x - 3 << endl;
}
else {
cout << 2 << ' ' << x - 2 << endl;
}
}
return 0;
}
Problem K. Keeping Rabbits 00:09 (-1) Solved by Dancepted
签到。稍微想一下发现期望重量跟第一天的重量线性相关,把所有的胡萝卜按重量分配即可。
因为担心不必要的精度问题用了long double结果交上去不知道为啥输出了-0.000000,WA了一发。
代码:
#include <bits/stdc++.h>
#define N 100005
using namespace std;
typedef double db;
db w[N];
int main() {
int T;
cin >> T;
while (T--) {
int n, k;
scanf("%d%d", &n, &k);
db sum = 0;
for (int i = 1; i <= n; i++) {
scanf("%lf", &w[i]);
sum += w[i];
}
for (int i = 1; i <= n; i++) {
w[i] += (db)k*w[i]/sum;
printf("%.6f%c", w[i], " \n"[i == n]);
}
}
return 0;
}
Problem F. Fixing Banners 00:36 (+) Solved by lh
签到。暴力枚举。
代码:
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define lowbit(x) ((-x) & x)
#define ffor(i, d, u) for (int i = (d); i <= (u); ++i)
#define _ffor(i, u, d) for (int i = (u); i >= (d); --i)
#define mst(array, Num, Kind, Count) memset(array, Num, sizeof(Kind) * (Count))
#define mp(x, y) make_pair(x, y)
#define fi first
#define se second
typedef long long ll;
typedef double db;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
typedef pair<db, db> pdd;
const db PI = acos(-1);
template <typename T>
inline void read(T &x)
{
x=0;char c;T t=1;while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c=='-'){t=-1;c=getchar();}do(x*=10)+=(c-'0');while((c=getchar())>='0'&&c<='9');x*=t;
}
template <typename T, typename... Args>
inline void read(T &x, Args &... args)
{
read(x), read(args...);
}
template <typename T>
inline void write(T x)
{
int len=0;char c[21];if(x<0)putchar('-'),x*=(-1);
do{++len;c[len]=(x%10)+'0';}while(x/=10);_ffor(i,len,1)putchar(c[i]);
}
const ll MO = 1e9 + 7;
const ll Inv2 = (MO + 1) / 2;
#define N 2000005
#define M 3000005
int T, cnt[6][6];
char a[6][N], mu[6] = {'h', 'a', 'r', 'b', 'i', 'n'};
bool flag[6] = {};
inline int ac()
{
read(T);
while (T--)
{
int b[6] = {0, 1, 2, 3, 4, 5};
ffor(i, 0, 5)
{
scanf("%s", a[i]);
ffor(j, 0, 5) cnt[i][j] = 0;
}
ffor(i, 0, 5)
{
int len = strlen(a[i]) - 1;
ffor(j, 0, len)
{
switch(a[i][j])
{
case 'h':
++cnt[i][0];
break;
case 'a':
++cnt[i][1];
break;
case 'r':
++cnt[i][2];
break;
case 'b':
++cnt[i][3];
break;
case 'i':
++cnt[i][4];
break;
case 'n':
++cnt[i][5];
break;
}
}
}
bool flag = false;
do
{
int tot = 0;
ffor(i, 0, 5) if (cnt[b[i]][i] > 0)++ tot;
else break;
if (tot == 6)
{
flag = true;
break;
}
} while (next_permutation(b, b + 6));
puts(flag ? "Yes" : "No");
}
return 0;
}
int main()
{
ac();
return 0;
}
Problem I. Interesting Permutation 01:48 (-1) Solved by Dancepted
不考虑a1是什么,而是考虑$a_{i}和a_{i-1}$之间的相对大小关系。
如果h序列是合法的话,最后n个数肯定是占据数轴上的一段长度为n的区间,这时再移动a1使得所有的数都落在[1, n]。
考虑h序列相邻两项
①$h_{i} < h_{i-1}$,不存在这样的序列,答案为0
②$h_{i} > h_{i-1}$,$a_{i}$比之前的最大值大$h_{i} - h_{i-1}$,或比之前的最小值小$h_{i} - h_{i-1}$,仅这两种选择,所以。
③$h_{i} = h_{i-1}$,$a_{i}$落在之前的最大值和最小值之间,选择的方案数为:[最小值,最大值]的区间长度 - $a_{i}$之前的数的个数(i - 1)
(WA了一发是因为脑抽把1e9+7写成了1e9+9,害得队友帮我写了一发暴力对拍了超久)
代码:O(n)
#include <bits/stdc++.h>
#define N 100005
#define md 1000000007
using namespace std;
typedef double db;
typedef long long ll;
int h[N];
int main() {
int T;
cin >> T;
while (T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &h[i]);
}
ll ans = 1;
if (h[1] != 0 || h[n] != n-1)
ans = 0;
for (int i = 2; i <= n; i++) {
if (h[i] < h[i-1]) {
ans = 0;
}
else if (h[i] == h[i-1]) {
ans = ans * (h[i] + 1 - (i - 1)) % md;
}
else if (h[i] > h[i-1]) {
ans = ans * 2 % md;
}
if (h[i]+1 <= i-1)
ans = 0;
if (ans == 0)
break;
}
cout << ans << endl;
}
return 0;
}
Problem E. Exchanging Gifts 02:19 (-1) Solved by Dancepted
因为礼物可以任意交换,所以如果知道了最后一个序列内各个数字出现的次数总和sum和数量最多的数字的数量$cnt_{max}$,那么答案肯定就是max(sum,2 × (sum - $cnt_{max}$)。
考虑如何统计各个数字的出现次数。
①如果最后一个序列是直接给出的,那么直接统计即可。
②如果最后一个序列是由之前的两个串给出的,那么对那两个串x和y分别统计。
然后是类似dp的做法,$f_{i}$表示第i个序列被统计的次数,那么②操作就相当于$f_{x} += f_{i}, f_{y} += f_{i}$
(WA了一发是因为f数组没有开long long,掌嘴)
代码:O(n + $\sum k$)
#include <bits/stdc++.h>
#define sz(x) (int)x.size()
#define N 1000005
using namespace std;
typedef double db;
typedef long long ll;
vector<int> nums[N];
ll mul[N];
int x[N], y[N];
unordered_map<int, ll> numcnt;
unordered_map<int, ll> :: iterator it;
int main() {
int T;
cin >> T;
while (T--) {
int n;
scanf("%d", &n);
numcnt.clear();
for (int i = 1; i <= n; i++) {
nums[i].clear();
mul[i] = 0;
x[i] = y[i] = -1;
}
mul[n] = 1;
for (int i = 1; i <= n; i++)
{
int act;
scanf("%d", &act);
if (act == 1)
{
int k;
scanf("%d", &k);
for (int j = 1; j <= k; j++)
{
int q;
scanf("%d", &q);
nums[i].push_back(q);
}
}
else if (act == 2)
{
scanf("%d%d", &x[i], &y[i]);
}
}
for (int i = n; i >= 1; i--) {
if (x[i] != -1) {
mul[x[i]] += mul[i];
mul[y[i]] += mul[i];
}
}
ll sum = 0, mx = 0;
for (int i = 1; i <= n; i++) {
if (x[i] == -1 && mul[i]) {
for (int &tmp : nums[i]) {
numcnt[tmp] += mul[i];
sum += mul[i];
mx = max(mx, numcnt[tmp]);
}
}
}
ll ans = min(sum, 2 * (sum - mx));
cout << ans << endl;
}
return 0;
}
比赛结束前我还在敲B题,奈何思路太乱了,实现起来太复杂,赛后调了半天还是过不去(最后还是惨兮兮去看了题解)。
UPD:B. Binary Numbers(dp+滚动数组优化)
函数$F_{k}(a, b)$相当于求a,b转化成长度为m的二进制串后求最长公共前缀lcp
然后可以发现两个数值相差越大,lcp越小。所以对于$[L_{i}, R_{i}]$这个区间,只要满足:
①:$lcp(A_{j}, L_{i}) <= lcp(A_{i}, L{i}),j < i$
②:$lcp(A_{j}, R_{i}) <= lcp(A_{i}, R{i}), j > i$
用$f_{i, j, k}$表示考虑了前i个区间,$A_{i}$和$L_{i+1}$的lcp为j,和$R_{i}$的lcp为k的价值。
然后暴力枚举$f_{i-1}$中满足条件的j和k进行转移即可。
空间不够,可以滚动数组优化一下。
代码:O($2^{m}m^{2}$)
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #define N 266666 // #define N 555 #define M 18 using namespace std; typedef long long ll; const int md = 100000007; inline int add(int a, int b) { int res = (a + b) % md; if (res < 0) res += md; return res; } inline int mul(int a, int b) { return (int)((ll)a * b % md); } inline int lcp(int a, int b, int m) { int res = 0; for (int i = m-1; i >= 0; i--) { if ((a & (1<<i)) == (b & (1<<i))) res++; else break; } return res; } int l[N], r[N]; int f[2][M][M]; int main() { int T; cin >> T; while (T--) { int m, n; cin >> m >> n; for (int i = 1; i <= n; i++) scanf("%d%d", &l[i], &r[i]); for (int i = 0; i < 2; i++) { for (int j = 0; j <= m; j++) { for (int k = 0; k <= m; k++) { f[i][j][k] = 0; } } } for (int i = l[1]; i <= r[1]; i++) { int lcpl = lcp(i, l[2], m), lcpr = lcp(i, r[1], m); f[1][lcpl][lcpr] = add(f[1][lcpl][lcpr], i); } for (int i = 2; i <= n; i++) { int id = i & 1; memset(f[id], 0, sizeof f[id]); for (int j = l[i]; j <= r[i]; j++) { int lcpl = lcp(j, l[i+1], m), lcpr = lcp(j, r[i], m); int prelcpr = lcp(j, r[i-1], m), prelcpl = lcp(j, l[i], m); for (int l1 = 0; l1 <= prelcpl; l1++) { for (int r1 = prelcpr; r1 <= m; r1++) { f[id][lcpl][lcpr] = add(f[id][lcpl][lcpr], mul(j, f[id^1][l1][r1])); } } } } ll ans = 0; for (int l = 0; l <= m; l++) { for (int r = 0; r <= m; r++) { ans = add(ans, f[n&1][l][r]); } } cout << ans << endl; } return 0; }
总结:
这场的几个罚时都不太应该,没有开long long(差点还踩了1e8+7的坑)、手滑写错了1e9+9、long double 输出-0.000000。。。
主要刚开始上手vscode,心理上有点不适应,所以写代码的时候有点暴躁?而且用的队友的电脑,没有头文件给我复制。。。
B题读题的时候太慢了,也是题目做的太少了,归纳能力太差劲。
然后xk这场状态不是很好的亚子(也可能是都花时间帮我debug了,不过有一说一,xk帮我找bug确实每次都能找出来)。
金区不像银、铜区,卡牌子的都只有一道题,开出来就高一档次,没开出来就没有。
金区那条线的题有很多,多过一道题就能金。然鹅我们队签完到进入银区后,因为没有学得很精的算法,基本没有能开出来的金牌题,还是有点难受的。