Rank
给我唐完了,
又名,【MX-S5】梦熊 NOIP 2024 模拟赛 1。
A. 王国边缘
好像简单倍增就做完了。
由于昨天 T5 在我脑海中留下了挥之不去的印象,今天一上来看到这题就发现是一个内向基环树森林。然后被硬控硬控硬控,最后一个小点加一点优化就能过没调出来,挂 30pts,菜菜菜菜菜。
注意到倍增理论复杂度是 \(\mathcal{O(n\log n)}\) 的,而我们基环树玩家的理论复杂度可以做到完全线性,所以一下午直接调出了 8.2k 基环树做法。
中途插曲还不少,包括但不限于判完完整环之后暴力查询过了,轻松卡成 \(\mathcal{O(n^2)}\);然后在环上加了前缀和优化,不过链上还是 \(\mathcal{O(n)}\) 跑,又过了;尝试交题解,被告知要贴代码,遂加入离线 dfs 优化,至此达成 \(\mathcal{O(n)}\)。
具体做法见此。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
ll n, m, q;
string s;
int las[N], fx[N], dfn[N], dt;
int hh[N], to[N], ne[N], cnt;
bool huan[N], vis[N], yz[N];
int bushu[N], daxiao[N], jieru[N];
ll len[N], w[N], dep[N], dis[N];
int newid[N], tot, st[N];
ll sumid[N];
ll ans[N];
namespace Wtask
{short main(){m %= mod;fo(i, 1, q){ll qd = qr, k = qr;qd %= mod;k %= mod;printf("%lld\n", (qd + k * m % mod) % mod);}return Ratio;}
}
int h1[N], t1[N], n1[N], tnc;
ll w1[N];
vector<pii> que[N];
namespace Wlixian
{int deep[N];ll shendu[N];inline void Wdfs(int u, int sd){for(pii qq : que[u]){int x = qq.fi, y = qq.se;ans[y] = (ans[y] + (shendu[u] - shendu[deep[sd - x]] + mod) % mod) % mod;}for(int i = h1[u]; i != -1; i = n1[i]){int v = t1[i];if(huan[v]) continue;deep[sd + 1] = v;shendu[v] = (shendu[u] + w1[i]) % mod;Wdfs(v, sd + 1);}}inline void Wwork(){fo(i, 1, n) if(huan[i]) deep[1] = i, Wdfs(i, 1);}
}
namespace Wisadel
{int siz[N];inline int Wfind(int x){if(x == fx[x]) return x;return fx[x] = Wfind(fx[x]);}inline void Wmerge(int u, int v){int _ = Wfind(u), __ = Wfind(v);if(siz[_] < siz[__]) fx[_] = __, siz[__] += siz[_];else fx[__] = _, siz[_] += siz[__];}inline void Wadd(int u, int v, ll val){to[++cnt] = v;w[cnt] = val;ne[cnt] = hh[u];hh[u] = cnt;}inline void Wda(int u, int v, ll val){t1[++tnc] = v;w1[tnc] = val;n1[tnc] = h1[u];h1[u] = tnc;}int tag = 1e9;inline void Wsearch(int u, int id, int tar){newid[u] = id;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == tar) return ;sumid[id + 1] = sumid[id] + w[i];Wsearch(v, ++tot, tar);}}inline void Wdfs(int u){yz[u] = 1;vis[u] = 1;dfn[u] = ++dt;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(vis[v]){tag = dfn[v];huan[u] = 1;sumid[++tot] = 0;st[Wfind(u)] = tot;Wsearch(v, ++tot, v);len[Wfind(u)] = (dep[u] - dep[v] + w[i] + mod) % mod;}else if(yz[v]) continue;else dep[v] = (dep[u] + w[i]) % mod, Wdfs(v);}vis[u] = 0;if(dfn[u] >= tag) jieru[u] = u, huan[u] = 1, daxiao[Wfind(u)]++;if(dfn[u] == tag) tag = 1e9;}inline void Wdfss(int u){yz[u] = 1;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(huan[v]) dis[u] = w[i], bushu[u] = 1, jieru[u] = v;else Wdfss(v), dis[u] = dis[v] + w[i], bushu[u] = bushu[v] + 1, jieru[u] = jieru[v];}}short main(){freopen("kingdom.in", "r", stdin), freopen("kingdom.out", "w", stdout);n = qr, m = qr, q = qr;fo(i, 1, n) fx[i] = i, siz[i] = 1;memset(h1, -1, sizeof h1);memset(hh, -1, sizeof hh);cin >> s;s = " " + s;int zaisuan = -1;fo(i, 1, n) if(s[i] == '1'){zaisuan = i - 1; break;}if(zaisuan == -1){fo(i, 1, q){ll qd = qr, k = qr;qd %= mod;k %= mod;printf("%lld\n", (qd + k) % mod);}return Ratio;}int ok = 1;fo(i, 1, n) if(s[i] != '1'){ok = 0; break;}if(ok) return Wtask::main();fo(i, zaisuan + 1, n) las[i] = s[i] == '1' ? i : las[i - 1];fo(i, 1, zaisuan) las[i] = las[n] - n;fo(i, 1, n){if(i + m <= n){ll dis;if(las[i + m] <= i){dis = 1;Wmerge(i + 1, i);Wadd(i, i + 1, dis);Wda(i + 1, i, dis);}else{dis = las[i + m] - i;Wmerge(las[i + m], i);Wadd(i, las[i + m], dis);Wda(las[i + m], i, dis);}}else if(m < n){int to = (m - (n - i)) % n;int zto = to;if(las[to] <= 0){zto = las[to] + n;if(zto <= i){ll dis = 1;zto = i + 1;if(zto > n) zto = 1;Wmerge(zto, i);Wadd(i, zto, dis);Wda(zto, i, dis);}else{ll dis = zto - i;Wmerge(zto, i);Wadd(i, zto, dis);Wda(zto, i, dis);}}else{ll dis = las[to] - i + n;Wmerge(las[to], i);Wadd(i, las[to], dis);Wda(las[to], i, dis);}}else{ll to = (m - (n - i)) % n;ll tim = (m - (n - i)) / n;if(!to) to += n, tim--;int zto = las[to];if(zto <= 0) zto += n, tim--;ll dis = (n - i + zto + tim * n % mod) % mod;Wmerge(zto, i);Wadd(i, zto, dis);Wda(zto, i, dis);}}fo(i, 1, n) if(!yz[i]) Wdfs(i);fill(yz + 1, yz + 1 + n, 0);fo(i, 1, n) if(!yz[i] && !huan[i]) Wdfss(i);fo(i, 1, q){ll qidian = qr, cishu = qr, res = 0;if(cishu == 0){ans[i] = qidian % mod;continue;}int dxqidian = qidian % n;if(!dxqidian) dxqidian = n;int belong = Wfind(dxqidian);res = qidian % mod;if(cishu < bushu[dxqidian]) que[dxqidian].P_B(M_P(cishu, i));else{cishu -= bushu[dxqidian];ll timesl = cishu / daxiao[belong];cishu -= timesl * daxiao[belong];timesl %= mod;res = (res + timesl * len[belong] % mod) % mod;res = (res + dis[dxqidian]) % mod;if(cishu){if(newid[jieru[dxqidian]] - st[belong] + cishu <= daxiao[belong]) res = ((res + sumid[newid[jieru[dxqidian]] + cishu]) % mod - sumid[newid[jieru[dxqidian]]] + mod) % mod;else{int zcc = newid[jieru[dxqidian]] + cishu - daxiao[belong];res = (res + ((sumid[zcc] - sumid[newid[jieru[dxqidian]]] + mod) % mod + len[belong]) % mod) % mod;}}}ans[i] = res;}Wlixian::Wwork();fo(i, 1, q) printf("%lld\n", ans[i]);return Ratio;}
}
signed main(){return Wisadel::main();}
// 佳墙坂诶迦币等渔塞
B. 买东西题
反悔贪心板子。
其实做完两个特殊性质就把正解启发得差不多了,结合一下就过了。
考虑物品按 \(a_i\) 升序,券按 \(w_i\) 升序,双指针扫,这样能够保证当前券可被之前的所有物品使用,方便反悔操作。
发现 \(a_i-b_i\) 实质上就是优惠价给的满减值,我们维护一个堆存放当前可选的满减值,每次操作只有取或不取。若从堆中取了满减,那么将 \(a_i-b_i\) 加入堆。这个操作还挺好理解的,如果 \(a_i-b_i\) 在之后某次抉择中成了最优决策,那么将 \(i\) 用的券给当前物品用,然后 \(i\) 直接用优惠价买一定是优的。
然后就做完了,复杂度 \(\mathcal{O(n\log n)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{char ch = getchar(); lx x = 0, f = 1;for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);return x * f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define ppp pair<pii, pii>
#define fi first
#define se second
#define M_P(x, y) make_pair(x, y)
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int n, m;
struct good{int a, b;} g[N];
struct rmm{int w, v;} c[N];
priority_queue<int, vector<int>, less<int> > q;
namespace Wisadel
{short main(){freopen("buy.in", "r", stdin), freopen("buy.out", "w", stdout);n = qr, m = qr;fo(i, 1, n) g[i].a = qr, g[i].b = qr;fo(i, 1, m) c[i].w = qr, c[i].v = qr;sort(g + 1, g + 1 + n, [](good A, good B){return A.a < B.a;});sort(c + 1, c + 1 + m, [](rmm A, rmm B){return A.w < B.w;});int j = 1; ll ans = 0;fo(i, 1, n){while(j <= m && c[j].w <= g[i].a) q.push(c[j++].v);if(q.empty() || g[i].a - g[i].b >= q.top()) ans += g[i].b;else{ans += g[i].a - q.top(); q.pop();q.push(g[i].a - g[i].b);}}printf("%lld\n", ans);return Ratio;}
}
signed main(){return Wisadel::main();}
// 佳墙坂诶迦币等渔塞
C. IMAWANOKIWA (Construction ver.)
大粪掏题。
D. 魔法少女们
魔法题。
末
为什么只写了两个题就发,你是不是想摆烂?
因为 T1 打了一个下午,T3 看起来就要改很长时间,怕来不及记录就先发了。
谁问你了?
?
开场直接被隔壁发现是原题,甚至三天前的比赛直接搬。然后因为没人做过所以正常进行,输麻了。
状态?感觉不是,可能主要因为 T1 打了太久被影响了心态。其实 T1 当时已经打了正解的 \(\frac{3}{4}\) 左右了,考虑当时只剩一个半小时就没继续看,结果错误是弱智取模,输麻了。
T2 感觉也不该是想不出来的,不过丁真
T3 T4 压根不想看,感觉 T4 讲的优化是那种学了一辈子用不上几次的那种,粉兔讲题更是直言我卡你那个了就不会再卡这个,有点玄学。
完结撒花~
没有