Rank
。
A. 选取字符串
签。
一眼想到动物园那个题面,kmp 求出的 next 数组实际上就是既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度。
那么可以想到,某个串除了它自身外,能选的 p/q 最长即为它的 next。更短的可选,一定只能是 next 的 next。以此类推,我们可以求得每个前缀作为 p/q 时可以被选的前缀数量,用组合数算一下即可。注意这个数量需要转移,以及边界情况的判断。时间复杂度 \(\mathcal{O(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 = 998244353;
int n, k;
int kmp[N], cnt[N], tim[N];
ll jc[N], ny[N], ans;
string s;
namespace Wisadel
{ll Wqp(ll x, int y){ll res = 1;while(y){if(y & 1) res = res * x % mod; x = x * x % mod; y >>= 1;}return res;}void Wpre(){cnt[0] = 2;int j = 0;fo(i, 2, n){while(j && s[j + 1] != s[i]) j = kmp[j];if(s[j + 1] == s[i]) j++;kmp[i] = j;cnt[j]++;}}int Wgettim(int x){if(tim[x]) return tim[x];if(!x) return tim[x] = 1;return Wgettim(kmp[x]) + 1;}ll C(int n, int m){if(n < m) return 0;return jc[n] * ny[m] % mod * ny[n - m] % mod;}short main(){freopen("string.in", "r", stdin), freopen("string.out", "w", stdout);k = qr; cin >> s; n = s.size(); s = " " + s;jc[0] = ny[0] = 1;fo(i, 1, n + 1) jc[i] = jc[i - 1] * i % mod;ny[n + 1] = Wqp(jc[n + 1], mod - 2);fu(i, n, 1) ny[i] = ny[i + 1] * (i + 1) % mod;Wpre();fo(i, 0, n) if(!tim[i]) tim[i] = Wgettim(i);fu(i, n, 0){ans = (ans + C(cnt[i] + (i != 0), k) * ((2 * tim[i] % mod - 1 + mod) % mod) % mod) % mod;cnt[kmp[i]] += cnt[i];}printf("%lld\n", ans);return Ratio;}
}
signed main(){return Wisadel::main();}
// Now there's only one thing I can do
// Fight until the end like I promised to
B. 取石子
一眼像原,但是并不会做。
赛时糊了 \(k\le 3\) 的部分分,然后 task1 多过了一个,所以 50pts。
正解学的 wang54321 的做法,有些神秘。考虑通过判断首次拿去某个位置的若干个石子后后手能否必胜来判断是否合法。通过根据当前的合法状态判断下一个可能的数量,可以达到 \(\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)
#define int ll
const int Ratio = 0;
const int N = 5e4 + 5;
const int mod = 998244353;
int n, k, tot, op;
ll a[N], sum[32];
vector<int> ans[N];
namespace Wisadel
{bool Wck(ll id, ll now, ll Q){ll zc = (a[id] - now) / (1 << (Q - 1)) + sum[Q];return zc & 1;}short main(){// freopen(".in", "r", stdin), freopen(".out", "w", stdout);freopen("nim.in", "r", stdin), freopen("nim.out", "w", stdout);n = qr, k = qr;fo(i, 1, n){a[i] = qr;fo(j, 1, 31) sum[j] += a[i] / (1 << (j - 1));}fo(i, 1, n){fo(j, 1, 31) sum[j] -= a[i] / (1 << (j - 1));ll now = 1, tim = 1;while(now <= min(a[i], k)){if(Wck(i, now, tim)) now += (1 << (tim - 1));else ans[i].P_B(now), now += (1 << tim), op = 1;tim++;}fo(j, 1, 31) sum[j] += a[i] / (1 << (j - 1));}if(!op) puts("0");else{puts("1");fo(i, 1, n) for(auto j : ans[i]) printf("%d %d\n", i, j);}return Ratio;}
}
signed main(){return Wisadel::main();}
// All talk and never answer
C. 均衡区间
考虑现将每个点作为极值的控制范围预处理出来,单调栈 \(\mathcal{O(n)}\) 完成。之后分别处理左右端点的答案,这样就转变成了一个二维数点的问题,其实就是扫描线,每个点的答案区间是好求的,树状数组维护一下就做完了,复杂度 \(\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 = 998244353;
int n, id;
int a[N], L[N], R[N], le[N], ri[N], t[N];
stack<int> s1, s2;
pii zc[N];
namespace Wisadel
{void Wpre(){fo(i ,1, n){while(s1.size() && a[s1.top()] <= a[i]) s1.pop();if(s1.empty()) L[i] = 1;else L[i] = s1.top() + 1;while(s2.size() && a[s2.top()] >= a[i]) s2.pop();if(s2.empty()) L[i] = min(L[i], 1);else L[i] = min(L[i], s2.top() + 1);s1.push(i), s2.push(i);}while(s1.size()) s1.pop();while(s2.size()) s2.pop();fu(i, n, 1){while(s1.size() && a[s1.top()] <= a[i]) s1.pop();if(s1.empty()) R[i] = n;else R[i] = s1.top() - 1;while(s2.size() && a[s2.top()] >= a[i]) s2.pop();if(s2.empty()) R[i] = max(R[i], n);else R[i] = max(R[i], s2.top() - 1);s1.push(i), s2.push(i);}}inline void Wadd(int x){for(; x <= n; x += (x & -x)) t[x]++;}inline int Wq(int x){int res = 0; for(; x; x -= (x & -x)) res += t[x]; return res;}short main(){freopen("interval.in", "r", stdin), freopen("interval.out", "w", stdout);n = qr, id = qr;fo(i, 1, n) a[i] = qr, L[i] = n;Wpre();fo(i, 1, n) zc[i] = M_P(L[i] - 1, i);sort(zc + 1, zc + 1 + n, [](pii A, pii B){return A.fi < B.fi;});int j = 1;fo(i, 1, n){while(j <= zc[i].fi) Wadd(R[j++]);ri[zc[i].se] = Wq(zc[i].se - 1);}fo(i, 1, n) t[i] = 0, zc[i] = M_P(R[i] + 1, i);sort(zc + 1, zc + 1 + n, [](pii A, pii B){return A.fi > B.fi;});j = n;fo(i, 1, n){while(j >= zc[i].fi) Wadd(L[j--]);le[zc[i].se] = Wq(n) - Wq(zc[i].se);}fo(i, 1, n) printf("%d ", le[i]); puts("");fo(i, 1, n) printf("%d ", ri[i]); puts("");return Ratio;}
}
signed main(){return Wisadel::main();}
// All talk and never answer
D. 禁止套娃
题还没看(逃
dp,明天一起改。
末
打的有点唐说实话,20min 写完 T1 代码结果忘改模数被硬控了将近 1h,然后死磕 T2,T3 正解最后有思路了没打完,遗憾离场。
最近越来越忙了,题解可能也不如之前详细了,也就大概说说做法和关键细节啥的。
发第二轮分数线了,一等线还挺低,√7 和 √6 分别卡掉一车人,还好我的得分比较玄学,根本卡不到我。
学了一年√6,只能说天赋几乎是没有的,有的全是这一年时间积累的经验吧。想开以后其实也是个挺好的结果了,至少比那些尝试的机会都没有的人强不是吗?
完结撒花~
还是图图