「赛后总结」高考集训:NOIP 模拟测试 A3
「赛后总结」高考集训:NOIP 模拟测试 A3
今日推歌
T1 谜之阶乘
思路
签到题。
根据数学直觉,\(a, b\) 相差不大。
根据数学直觉,\(n^{\tfrac{1}{b - a}}\) 和 \(\dfrac{a + b}{2}\)相差不大(\(\leftarrow\) 计算器带给我的)。
于是暴力枚举 \(b - a\),找到 \(a\) 然后 check 一下。
代码
点击查看代码
const ll N = 110;
namespace SOLVE {
ll n, cnt;
std::pair <ll, ll> ans[N];
inline ll rnt () {
ll x = 0, w = 1; char c = getchar ();
while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
return x * w;
}
inline void In () {
n = rnt (), cnt = 0;
return;
}
inline void Solve () {
if (n != 1) cnt = 1, ans[1].first = n, ans[1].second = n - 1;
_for (i, 2, 64) {
ll tmp = std::floor (pow (n, 1.0 / i)), s = 1, f = 0;
tmp = tmp - i / 2;
while (s < n){
++tmp, s = 1;
if (tmp <= 0) continue;
_for (j, 0, i - 1) {
s *= (tmp + j);
if (s > n || s < 0ll) break;
}
if (s == n) break;
}
if (tmp <= 1) break;
if (s == n) ++cnt, ans[cnt].first = tmp + i - 1, ans[cnt].second = tmp - 1;
}
return;
}
inline void Out () {
std::sort (ans + 1, ans + cnt + 1);
printf ("%lld\n", cnt ? cnt : -1);
_for (i, 1, cnt) printf ("%lld %lld\n", ans[i].first, ans[i].second);
return;
}
}
T2 子集
思路
好厉害的构造!
首先特判掉不合法情况:
- \(n = k.\)
- \(n = 1.\)
- \(k\nmid \frac{n(n+1)}2.\)
然后如果 \(\frac{n}{k}\) 为偶数就蛇形填。
否则你先把前三列填出来,然后再蛇形填。
有点。难描述。建议。直接看码。
这个常数不太好不放了,push_back ()
和 clear ()
太多的缘故?
代码
点击查看代码
const ll N = 1e6 + 10;
namespace SOLVE {
ll n, k, yn; std::vector <ll> ans[N];
inline ll rnt () {
ll x = 0, w = 1; char c = getchar ();
while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
return x * w;
}
inline void In () {
n = rnt (), k = rnt ();
return;
}
inline void Solve () {
_for (i, 1, k) ans[i].clear ();
if (k == 1) {
_for (i, 1, n) ans[1].push_back (i);
return;
}
if (n == 1 || n == k) { yn = 1; return; }
if ((n * (n + 1) / 2) % k) { yn = 1; return; }
if ((n / k) & 1) {
ll t = 0, qwq = (1 + 3 * k) * 3 / 2;
_for (i, 1, k) ans[i].push_back (++t);
_for (i, (k + 1) / 2 + 1, k) ans[i].push_back (++t);
_for (i, 1, (k + 1) / 2) ans[i].push_back (++t);
_for (i, 1, k) ans[i].push_back (qwq - ans[i][0] - ans[i][1]);
t = 3 * k;
_for (i, 4, n / k) {
if (i & 1) _for (j, 1, k) ans[j].push_back (++t);
else for_ (j, k, 1) ans[j].push_back (++t);
}
}
else {
ll t = 0;
_for (i, 1, n / k) {
if (i & 1) _for (j, 1, k) ans[j].push_back (++t);
else for_ (j, k, 1) ans[j].push_back (++t);
}
}
return;
}
inline void Out () {
if (yn) puts ("No"), yn = 0;
else {
puts ("Yes");
_for (i, 1, k) {
far (j, ans[i]) printf ("%lld ", j);
puts ("");
}
}
return;
}
}
T3 混凝土粉末
思路
赛时用的主席树,复杂度是 \(O(q\log_2q\log_2n)\)的。因为差分所以常数优于他人,多拿了 5pts。
update:差分的人严格多于不差分的,那你们就是纯粹常数没我好 [骄傲]。
考虑离线并差分,用一个树状数组维护在前 \(i\) 次操作后,当前扫到的数的大小。
具体的,从 \(1\) 扫到 \(n\),遇到一个 \(1\) 操作的左端点就把操作的值加上,遇到一个 \(1\) 操作的右端点就把操作的值减去,然后遇到一个 \(2\) 操作就二分查找哪次操作后符合要求。
代码
点击查看代码
const int N = 2e6 + 10;
namespace TREEARRAY {
class TreeArray {
private:
ll b[N];
inline int lowbit (int x) { return x & (-x); }
public:
int q;
inline void Update (int x, ll y) {
while (x <= q) b[x] += y, x += lowbit (x);
return;
}
inline ll Query (int x) {
ll sum = 0;
while (x > 0) sum += b[x], x -= lowbit (x);
return sum;
}
};
}
namespace SOLVE {
int n, q, vis[N], ans[N], cm, cq;
TREEARRAY::TreeArray tr;
class TMP {
public:
int x; ll y; int id;
inline bool operator < (TMP another) const {
return x < another.x;
}
} mo[N], qu[N];
inline ll rnt () {
ll x = 0, w = 1; char c = getchar ();
while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
return x * w;
}
inline void In () {
n = rnt (), tr.q = q = rnt ();
_for (i, 1, q) {
ll op = rnt ();
if (op == 1) {
int l = rnt (), r = rnt (); ll h = rnt ();
mo[++cm] = (TMP){l, h, i}, mo[++cm] = (TMP){r + 1, -h, i};
}
else {
int x = rnt (); ll y = rnt ();
qu[++cq] = (TMP){x, y, i};
vis[i] = 1;
}
}
return;
}
inline void Solve () {
std::sort (mo + 1, mo + cm + 1);
std::sort (qu + 1, qu + cq + 1);
int p1 = 0, p2 = 0;
_for (i, 1, n) {
while (mo[p1 + 1].x == i) ++p1, tr.Update (mo[p1].id, mo[p1].y);
while (qu[p2 + 1].x == i) {
++p2;
if (tr.Query (qu[p2].id) < qu[p2].y) {
ans[qu[p2].id] = 0;
continue;
}
int l = 1, r = ans[qu[p2].id] = qu[p2].id;
while (l <= r) {
bdmd;
if (tr.Query (mid) < qu[p2].y) l = mid + 1;
else ans[qu[p2].id] = mid, r = mid - 1;
}
}
}
return;
}
inline void Out () {
_for (i, 1, q) if (vis[i]) printf ("%d\n", ans[i]);
return;
}
}
T4 排水系统
思路
设 \(f_{u}\) 表示一条边不堵时 \(u\) 点的流量,\(d_{u}\) 表示 \(u\) 点出度,\(g_{u}\) 表示 \(u\) 点的期望流量。
考虑堵一个 \((u, v)\) 造成的影响。
\(v\) 流量会少 \(\dfrac{f_{u}}{d_{u}}\),\(u\) 指向的其他点流量会多 \(\dfrac{f_{u}}{d_{u}(d_{u} - 1)}\)。
直接改很慢啊!于是我们把 \(u\) 指向的其他点流量合起来加到 \(u\) 上,给 \(v\) 多减点。
具体一点,\(v\) 流量会少 \(\dfrac{f_{u}}{d_{u}(d_{u} - 1)} + \dfrac{f_{u}}{d_{u}}\),\(u\) 流量会多 \(\dfrac{f_{u}}{d_{u} - 1}\)。
代码
点击查看代码
const ll N = 5e5 + 10, P = 998244353;
namespace SOLVE {
ll n, m, r, k, sum, in[N], vis[N], f[N], g[N];
class Edge { public: ll v, w; };
std::vector <Edge> tu[N];
inline ll rnt () {
ll x = 0, w = 1; char c = getchar ();
while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
return x * w;
}
inline ll FastPow (ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1) ans = ans * a % P;
a = a * a % P, b >>= 1;
}
return ans;
}
inline void TopoSort () {
std::queue <ll> q;
_for (i, 1, n) {
f[i] = g[i] = 0, vis[i] = in[i];
if (!in[i]) q.push (i), f[i] = g[i] = 1;
}
while (!q.empty ()) {
ll u = q.front (), cs = tu[u].size (), csa = cs - 1; q.pop ();
cs = FastPow (cs, P - 2), csa = FastPow (csa, P - 2);
far (qwq, tu[u]) {
ll p = qwq.w * sum % P;
g[u] = (g[u] + f[u] * csa % P * p % P) % P;
g[qwq.v] = (g[qwq.v] - f[u] * cs % P * (csa + 1) % P * p % P + P) % P;
if (!(--vis[qwq.v])) q.push (qwq.v);
}
far (qwq, tu[u]) {
g[qwq.v] = (g[qwq.v] + g[u] * cs % P) % P;
f[qwq.v] = (f[qwq.v] + f[u] * cs % P) % P;
}
}
return;
}
inline void In () {
n = rnt (), m = rnt (), r = rnt (), k = rnt ();
_for (i, 1, k) {
ll u = rnt (), v = rnt (), w = rnt ();
tu[u].push_back ((Edge){v, w});
sum = (sum + w) % P, ++in[v];
}
return;
}
inline void Solve () {
sum = FastPow (sum, P - 2);
TopoSort ();
return;
}
inline void Out () {
_for (i, 1, n) if (tu[i].empty ()) printf ("%lld ", g[i]);
puts ("");
return;
}
}