在家看 NOI2024 题面后打的神金代码
Day 1 集合
#include <iostream>
#include <map>
#include <random>
using namespace std;
const int N = 2e5 + 10, M = 6e5 + 10;
int n, m, q, x, y, a[N][3], b[N][3], cur, req[N];
using ull = unsigned long long;
map<ull, int> mp;
random_device rd;
mt19937 mt(rd());
ull sg[N], pa[M], pb[M];
void acs(int x, int v)
{
for (int i = 0; i < 3; i++)
{
cur += !mp[pa[a[x][i]]];
mp[pa[a[x][i]]]--;
cur -= !mp[pa[a[x][i]]];
pa[a[x][i]] += v * sg[x];
cur += !mp[pa[a[x][i]]];
mp[pa[a[x][i]]]++;
cur -= !mp[pb[b[x][i]]];
cur += !mp[pb[b[x][i]]];
mp[pb[b[x][i]]]++;
cur -= !mp[pb[b[x][i]]];
pb[b[x][i]] += v * sg[x];
cur += !mp[pb[b[x][i]]];
mp[pb[b[x][i]]]--;
cur -= !mp[pb[b[x][i]]];
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", a[i] + j);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", b[i] + j);
}
}
for (int i = 1; i <= m; i++)
{
sg[i] = mt();
}
for (int i = 1, j = 1; i <= n; i++)
{
acs(i, 1);
while (cur)
acs(j, -1), j++;
req[i] = j;
}
for (int i = 1; i <= q; i++)
{
scanf("%d%d", &x, &y);
puts(req[y] <= x ? "Yes" : "No");
}
}
笑点解析:看不懂题解自己重新想了一个哈希方法,以及有一行把 pa
打成 pb
了还能过样例
#include <iostream>
#include <map>
#include <random>
using namespace std;
const int N = 2e5 + 10, M = 6e5 + 10;
int n, m, q, x, y, a[N][3], b[N][3], cur, req[N];
using ull = unsigned long long;
map<ull, int> mp;
random_device rd;
mt19937 mt(rd());
ull sg[N], pa[M], pb[M];
void upd(ull x, int v)
{
cur += !mp[x];
mp[x] += v;
cur -= !mp[x];
}
void acs(int x, int v)
{
for (int i = 0; i < 3; i++)
{
upd(pa[a[x][i]], -1);
pa[a[x][i]] += v * sg[x];
upd(pa[a[x][i]], 1);
upd(pb[b[x][i]], 1);
pb[b[x][i]] += v * sg[x];
upd(pb[b[x][i]], -1);
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", a[i] + j);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", b[i] + j);
}
}
for (int i = 1; i <= m; i++)
{
sg[i] = mt();
}
for (int i = 1, j = 1; i <= n; i++)
{
acs(i, 1);
while (cur)
acs(j, -1), j++;
req[i] = j;
}
for (int i = 1; i <= q; i++)
{
scanf("%d%d", &x, &y);
puts(req[y] <= x ? "Yes" : "No");
}
}
笑点解析:封装一个函数后直接省下 \(160\) 字节,而且只有 \(15\) 分
#include <iostream>
#include <map>
#include <random>
using namespace std;
const int N = 2e5 + 10, M = 6e5 + 10;
int n, m, q, x, y, a[N][3], b[N][3], cur, req[N];
using ull = unsigned long long;
map<ull, int> mp;
random_device rd;
mt19937 mt(rd());
ull sg[N], pa[M], pb[M];
void upd(ull x, int v)
{
cur += !mp[x];
mp[x] += v;
cur -= !mp[x];
}
void acs(int x, int v)
{
for (int i = 0; i < 3; i++)
{
upd(pa[a[x][i]], -1);
pa[a[x][i]] += v * sg[x];
upd(pa[a[x][i]], 1);
upd(pb[b[x][i]], 1);
pb[b[x][i]] += v * sg[x];
upd(pb[b[x][i]], -1);
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", a[i] + j);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", b[i] + j);
}
}
for (int i = 1; i <= n; i++)
{
sg[i] = mt();
}
for (int i = 1, j = 1; i <= n; i++)
{
acs(i, 1);
while (cur)
acs(j, -1), j++;
req[i] = j;
}
for (int i = 1; i <= q; i++)
{
scanf("%d%d", &x, &y);
puts(req[y] <= x ? "Yes" : "No");
}
}
笑点解析:这个代码和上一个代码只差一个字母,但是它 \(100\) 分
Day 2 分数
#include <iostream>
#include <numeric>
#include <set>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int n, m;
using ll = long long;
struct frac
{
ll x, y;
const bool valid() const
{
return x > 0 and x <= n and y <= m and (x * 2 <= y or x >= y * 2);
}
const frac rev() const
{
return {y, x};
}
const frac ad(ll v) const
{
return {x + v * y, y};
}
bool operator<(const frac &ele) const
{
if (x != ele.x)
return x < ele.x;
return y < ele.y;
}
};
set<frac> st;
queue<frac> q;
int main()
{
scanf("%d%d", &n, &m);
st.insert({1, 2});
q.push({1, 2});
while (q.size())
{
frac tmp = q.front();
q.pop();
for (auto &i : {tmp.rev(), tmp.ad(2), tmp.ad(-2)})
{
if (!i.valid() or st.count(i))
continue;
st.insert(i);
q.push(i);
}
}
printf("%d\n", st.size());
// for (auto &i : st)
// {
// printf("%lld %lld\n", i.x, i.y);
// }
}
笑点解析:不剪枝有 \(50\) 分
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int n, m;
using ll = long long;
struct frac
{
ll x, y;
const bool valid() const
{
return x > 0 and x <= n and y <= m and (x * 2 <= y or x >= y * 2);
}
const frac rev() const
{
return {y, x};
}
const frac ad(ll v) const
{
return {x + v * y, y};
}
bool operator<(const frac &ele) const
{
if (y != ele.y)
return y < ele.y;
return x < ele.x;
}
};
queue<frac> q;
ll res = 2;
int main()
{
scanf("%d%d", &n, &m);
q.push({1, 2});
q.push({2, 1});
while (q.size())
{
frac tmp = q.front();
q.pop();
for (auto &i : {tmp.ad(2), tmp.rev().ad(2).rev()})
{
if (!i.valid())
continue;
res++;
q.push(i);
}
}
printf("%lld\n", res);
}
笑点解析 \(2\):用了 Calkin-Wilf 树然后不加剪枝就变成了 \(85\) 分