模拟赛小结:2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)
比赛链接:传送门
两个半小时的时候横扫了铜、银区的所有题,签到成功混进金区。奈何后面没能开出新的题。
最后一个小时的时候xk灵机一动想出了D题的做法,讨论了一波感觉可行,赶紧去敲。结束前2分钟终于过了样例结果WA3。
赛后10分钟,xk改了两个bug就过了D。。。。离金最近的一场(又来?)。
Problem B. Baby Bites 00:11 (-1) Solved by Dancepted
很明显的签到题,题面短又水。(那你还WA了一发?555我错了,我把循环里的j手滑写成了i)
代码:
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 100005
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
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 = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
int main() {
int n;
cin >> n;
bool ans = true;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
if (s[0] == 'm') {
continue;
}
int a = 0;
for (int j = 0; j < sz(s); j++)
a = a * 10 + s[j] - '0';
if (a != i) {
ans = false;
}
}
if (ans) {
puts("makes sense");
}
else {
puts("something is fishy");
}
return 0;
}
Problem C. Code Cleanups 00:27 (+) Solved by xk
队友签的到。
代码:
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 2505
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
int a[400];
int main()
{
fast;
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> a[i];
}
int cnt = 0, p = 0;
int dirty = 0;
int ans = 0;
for(int i = 1; i <= 365 && p < n; i++)
{
dirty += cnt;
if(a[p] == i) {
p++;
cnt++;
}
if(dirty + cnt >= 20) {
ans++;
cnt = dirty = 0;
}
}
if(cnt) ans++;
cout << ans << endl;
}
Problem K. King's Colors 01:13 (+) Solved by Dancepted
xk乍一看是个树形dp,直接丢了给我。我也差点被xk带偏。
实际上大概是类似容斥一样的线性dp:
首先,有n个节点的树,使用了 <= i种颜色的染色方案数为 $ i * (i-1)^{n-1} $,每个节点只要和父节点不同即可,有i-1种方案,而根没有限制,有i种方案。
设$f_{i}$为恰好使用了i种颜色的染色方案数:
那么$f_{i} = i * (i-1)^{n-1} - \sum_{j=1}^{i-1} C_{i}^{j} * f_{j}$。就是 <= i种颜色的染色方案数 - $\sum$(从i种颜色中选j种颜色的方案数) * j种颜色的染色方案数。
代码:$O(n^{2})$
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 2505
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
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 = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
#define md 1000000007
inline ll mul(ll a) {return a;}
template <typename... Args>
inline ll mul(ll a, Args ... args) {return a*mul(args...) % md;}
inline ll add(ll a) {return a;}
template <typename... Args>
inline ll add(ll a, Args ... args) {
ll res = (a+add(args...)) % md;
if (res < 0) res += md;
return res;
}
inline ll fpow(ll a, ll p) {
ll res = 1;
for (; p; p >>= 1) {
if (p&1) res = mul(res, a);
a = mul(a, a);
}
return res;
}
inline ll getInv(ll x) { return fpow(x, md-2); }
#define MAXN 2505
ll fac[MAXN], inv[MAXN];
ll C(ll n, ll m) { if (m < 0 || m > n) return 0; return mul(fac[n], mul(inv[n-m], inv[m])); }
ll P(ll n, ll m) { return mul(fac[n], fac[n-m]); }
void init() {
fac[0] = 1; for (int i = 1; i < MAXN; i++) fac[i] = mul(fac[i-1], i);
inv[MAXN-1] = fpow(fac[MAXN-1], md-2); for (int i = MAXN-2; i >= 0; i--) inv[i] = mul(inv[i+1], i+1);
}
ll f[N];
int main() {
init();
int n; ll k; read(n, k);
for (int i = 2; i <= n; i++) {
int a; read(a);
}
for (int i = 2; i <= k; i++) {
f[i] = mul(i, fpow(i-1, n-1));
for (int j = 2; j <= i-1; j++) {
ll tmp = mul(C(i, j), f[j]);
f[i] = add(f[i], -tmp);
}
}
ll ans = f[k];
cout << ans << endl;
return 0;
}
Problem H. House Lawn 01:39 (+) Solved by xk
在我和lh捣鼓J的时候,xk已经开出了I和H,抢键盘上机秒题。
代码:
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define sz(x) ((int)x.size())
#define forn(i, n) for(int i = 0; i < (n); i++)
#define forab(i, a, b) for(int i = (a); i <= (b); i++)
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
char s[105][100];
ll p[105], c[105], t[105], r[105];
int main()
{
ll l, n;
cin >> l >> n;
forn(i, n)
{
scanf("\n%[^,],%lld,%lld,%lld,%lld", s[i], &p[i], &c[i], &t[i], &r[i]);
}
ll mxp = 1e18;
forn(i, n)
{
if(p[i] > mxp) continue;
if(c[i] * t[i] * 10080 >= l * (r[i] + t[i]))
{
mxp = p[i];
}
}
if(mxp == 1e18) {
return puts("no such mower") * 0;
}
forn(i, n)
{
if(p[i] == mxp && (c[i] * t[i] * 10080 >= l * (r[i] + t[i])))
{
puts(s[i]);
}
}
}
Problem J. Jumbled String 01:56 (-3) Solved by lh & Dancepted
lh在01:06的时候就已经写好了代码,但是有几个特殊值没有考虑到,差点弃题。
直接计算0和1的数量n和m,得到总数量n+m,$C_{n+m}^{2}$ = a + b + c + d。如果成立就贪心地在1之间插入0,使得01的数量是正确的,那么10的数量肯定是正确的。
需要特判abcd全0或者有3个0的情况。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
long long a, b, c, d, n, m;
long long sum[100005] = {};
int main()
{
cin >> a >> b >> c >> d;
if (a == 0 && b == 0 && c == 0 && d == 0)
{
puts("1");
return 0;
}
bool flag = false;
if (a == 0 && d == 0)
n = 1, m = 1;
else
{
if (a == 0)
n = 1;
else
{
n = sqrt(a * 2);
for (long long i = max(n - 10, 0ll); i <= n + 10; ++i)
{
if (i * (i - 1ll) == a * 2)
{
n = i, flag = true;
break;
}
}
if (!flag)
{
puts("impossible");
return 0;
}
}
if (d == 0)
m = 1;
else
{
m = sqrt(d * 2), flag = false;
for (long long i = max(m - 10, 0ll); i <= m + 10; ++i)
{
if (i * (i - 1ll) == d * 2)
{
m = i, flag = true;
break;
}
}
if (!flag)
{
puts("impossible");
return 0;
}
}
}
if (c == 0 && b == 0)
{
if (a == 0 && d)
{
while (m--)putchar('1');
return 0;
}
else if (a && d == 0)
{
while (n--)putchar('0');
return 0;
}
}
long long tot = n + m;
if (a + b + c + d != (tot - 1ll) * tot / 2)
{
puts("impossible");
return 0;
}
for (long long i = 0; i < m; ++i)
{
sum[i] = b / (m - i);
b %= (m - i), n -= sum[i];
if (b == 0 || n == 0)
break;
}
if (b)
{
puts("impossible");
return 0;
}
for (int i = 0; i < m; ++i)
{
while (sum[i])putchar('0'), --sum[i];
putchar('1');
}
while (n)putchar('0'), --n;
return 0;
}
/*
1 4 2 3
*/
Problem I. Intergalactic Bidding 02:32 (+) Solved by Dancepted & xk
就是个cf div2的a、b题难度的大水题,但是要大数。
自信满满地打开eclipse发现大数不会读入,不会重载小于号排序。。。
只能老老实实切回vscode,掏出尘封多年的c++大数板子,一字一句地抄上去(模拟现场赛)。
代码:O(n*logn*logs)
#include <iostream>
#include <istream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 1005
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
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 = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
struct bigInt{
int len, d[N];
void clean() {
while (len > 1 && !d[len-1]) len--;
}
bigInt() {memset(d, 0, sizeof d); len = 1;}
bigInt(int num) {*this = num;}
bigInt operator = (int num) {
char s[20];
sprintf(s, "%d", num);
*this = s;
return * this;
}
bigInt operator = (const char* num) {
memset(d, 0, sizeof d);
len = strlen(num);
for (int i = 0; i < len; i++)
d[i] = num[len-1-i] - '0';
clean();
return *this;
}
bool operator < (const bigInt& b) const {
if (len != b.len) return len < b.len;
for (int i = len-1; i >= 0; i--)
if (d[i] != b.d[i])
return d[i] < b.d[i];
return false;
}
bool operator == (const bigInt& b) const { return !(b < *this) && !(*this < b); }
bigInt operator + (const bigInt& b) const {
bigInt c = *this;
c.len = max(len, b.len) + 1;
for (int i = 0; i < c.len; i++) {
c.d[i] += b.d[i];
c.d[i+1] += c.d[i] / 10;
c.d[i] %= 10;
}
c.clean();
return c;
}
bigInt operator - (const bigInt& b) {
bigInt c = *this;
int i;
for (i = 0; i < b.len; i++) {
c.d[i] -= b.d[i];
if (c.d[i] < 0) c.d[i] += 10, c.d[i+1]--;
}
while (c.d[i] < 0) c.d[i++] += 10, c.d[i]--;
c.clean();
return c;
}
};
istream& operator >> (istream& in, bigInt& x) {
string s; in >> s;
x = s.c_str();
return in;
}
struct Node{
string name;
bigInt val;
bool operator < (const Node& x) const {
return val < x.val;
}
}nodes[N];
vector <string> ans;
int main() {
ios::sync_with_stdio(), cin.tie(0), cout.tie(0);
int n; bigInt sum;
cin >> n >> sum;
for (int i = 1; i <= n; i++) {
cin >> nodes[i].name >> nodes[i].val;
}
sort(nodes+1, nodes+1+n);
for (int i = n; i >= 1; i--) {
if (nodes[i].val < sum || nodes[i].val == sum) {
sum = sum - nodes[i].val;
ans.push_back(nodes[i].name);
}
}
if (sum.len == 1 && sum.d[0] == 0) {
cout << ans.size() << endl;
for (string& s: ans) {
cout << s << endl;
}
}
else {
cout << 0 << endl;
}
return 0;
}
补题:
Problem E. Explosion Exploit(最短路 + 二分答案 + dp)
先跑n次Dijkstra得到两两之间的距离$dis_{i,j}$。
然后二分答案+dp确认。
$dp_{i}$表示在答案为mid时,第i个订单允许的最晚出发时间。
枚举骑手在送第i单时,连续送到第j个订单结束,才回披萨店取餐,则对于所有的k(i <= k <= j),满足下面条件的最大值,是$dp_{i}$的一个可能的值:
$dp_{i} + dis_{1, u_{i}} + \sum_{l=i+1}^{k}dis{u_{l}, u_{l-1}} + dis_{1, u_{k}} <= dp_{k+1}$。
最后的$dp_{i}$就是对所有的可能值取max。
如果存在$dp_{i} < t_{i}$,则说明不存在方案使得第i个单子在答案为mid时能被准时送到,则不可行。否则可行。
代码:$O(n^{2}logn)$
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define sz(x) ((int)x.size())
#define forn(i, n) for(int i = 0; i < (n); i++)
#define forab(i, a, b) for(int i = (a); i <= (b); i++)
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 1005;
struct edge
{
int to;
ll len;
};
struct node
{
int id;
ll dis;
bool operator < (const node &a) const {
return dis > a.dis;
}
};
ll dis[maxn][maxn];
vector<edge> g[maxn];
void dij(int x)
{
priority_queue<node> q;
memset(dis[x], 0x3f, sizeof(dis[x]));
dis[x][x] = 0;
q.push(node{x, 0});
while(!q.empty())
{
node t = q.top(); q.pop();
int u = t.id;
if(t.dis > dis[x][u]) continue;
forn(i, sz(g[u]))
{
edge &e = g[u][i];
if(dis[x][e.to] > dis[x][u] + e.len)
{
dis[x][e.to] = dis[x][u] + e.len;
q.push(node{e.to, dis[x][e.to]});
}
}
}
}
int n, m, k;
ll s[maxn], u[maxn], t[maxn];
ll dp[maxn];
bool check(ll x)
{
// cout << x << endl;
dp[k + 1] = 1e16;
for(int i = k; i > 0; i--)
{
ll sumdis = dis[1][u[i]];
ll temp = s[i] + x - sumdis;
dp[i] = min(s[i] + x - sumdis, dp[i + 1] - 2 * dis[1][u[i]]);
for(int j = i + 1; j <= k; j++)
{
sumdis += dis[u[j]][u[j - 1]];
temp = min(temp, s[j] + x - sumdis);
ll tt = min(temp, dp[j + 1] - dis[1][u[j]] - sumdis);
if(tt >= t[j])
dp[i] = max(dp[i], tt);
}
if (dp[i] < t[i]) return false;
// cout << dp[i] << ' ';
}
// cout << endl;
return dp[1] >= 0;
}
int main()
{
fast;
cin >> n >> m;
forn(i, m)
{
int u, v; ll l;
cin >> u >> v >> l;
g[u].push_back(edge{v,l});
g[v].push_back(edge{u,l});
}
forab(i, 1, n)
dij(i);
cin >> k;
forab(i, 1, k)
{
cin >> s[i] >> u[i] >> t[i];
}
ll l = -1, r = 1e16;
while (r - l > 1)
{
ll mid = (l + r) / 2;
if(check(mid))
r = mid;
else
l= mid;
}
cout << r << endl;
return 0;
}
Problem D. Delivery Delays(状压dp)
直接状压的话空间是$7^{10} ≈ 2e9$,考虑对1-6数值的数量状压。
这样可以用dp来计算空间复杂度,5个人分到6个数值250左右个方案。两边有10个人,平方一下大概是5e4个。
实现的话用dfs比较方便,用map记忆化一下就好(vscode不知为啥不能用unordered_map)。
参考博客:传送门
代码:
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <type_traits>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <iomanip>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define N 5
#define M 100005
#define INF 0x3f3f3f3f
#define mk(x) (1<<x) // be conscious if mask x exceeds int
#define sz(x) ((int)x.size())
#define upperdiv(a,b) (a/b + (a%b>0))
#define mp(a,b) make_pair(a, b)
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef double db;
/** fast read **/
template <typename T>
inline void read(T &x) {
x = 0; T fg = 1; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') fg = -1;
ch = getchar();
}
while (isdigit(ch)) x = x*10+ch-'0', ch = getchar();
x = fg * x;
}
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 = -x;
do{++len; c[len] = x%10 + '0';} while (x /= 10);
for (int i = len; i >= 1; i--) putchar(c[i]);
}
template <typename T, typename... Args>
inline void write(T x, Args ... args) { write(x), write(args...); }
int a[2][7];
ll haxi() {
ll res = 0;
for (int i = 0; i < 2; i++) {
for (int j = 1; j <= 6; j++) {
res = res * 10 + a[i][j];
}
}
return res;
}
map<ll, db> MP;
// unordered_map<ll, db> MP;
db dfs(ll sta, int resd) {
if (MP.count(sta)) return MP[sta];
if (sta < 1000000)
return MP[sta] = 1;
if (resd == 0)
return MP[sta] = 0;
int sum = 0;
for (int i = 0; i < 2; i++) {
for (int j = 1; j <= 6; j++) {
sum += a[i][j];
}
}
db res = 0;
for (int i = 0; i < 2; i++) {
for (int j = 1; j <= 6; j++) {
if (!a[i][j])
continue;
a[i][j]--, a[i][j-1]++;
db tmp = dfs(haxi(), resd-1);
a[i][j]++, a[i][j-1]--;
res += (db)a[i][j] / sum * tmp;
}
}
return MP[sta] = res;
}
int main() {
int n, m, d; read(n, m, d);
for (int i = 1; i <= n; i++) {
int x; read(x);
a[1][x]++;
}
for (int i = 1; i <= m; i++) {
int x; read(x);
a[0][x]++;
}
db ans = dfs(haxi(), d);
printf("%.8lf\n", ans);
return 0;
}
总结:
切完银牌题之后有点飘没有静下心思想E的做法,优化状态的数量是想到了的,但是没有底气继续写。
xk写D的时候我觉得xk的debug手法太糙了,忍不住抢了键盘qwq,而xk赛后10分钟改了两个bug就过了D,这里我应该也要背个锅吧,痛失金牌。
lh今天好像掉线了?