2025-11-19 ZYZ28-NOIP-XiaoMao Round 33550336 hetao1733837的record
2025-11-19 ZYZ28-NOIP-XiaoMao Round 33550336 hetao1733837的record
比赛链接:
A.C.Competitive Fishing
原题链接1:CF2042C Competitive Fishing
原题链接2:2042C Codeforces
提交链接:航海马(sail)
分析
这是个黄?***********************************
原始思路:$01$可能是一组,或者$0$应该不太能自己成为一组,特别是最后一组……在一组中尽可能$1$的个数大于$0$的个数。
正确思路:艹,并非从序列本身出发考虑。好的,后半部分猜想需要改动,然后得出重要结论:每个断点后对答案的贡献是$1$的个数减去$0$的个数。
那么,只需从后往前枚举断点并算出$n-1$个断点对答案的贡献,从大到小贪心地取即可。
呃……CF题好在比较思维,也坏在思维比较意识流。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int t;
int n, k;
string s;
int w[N];
int main(){
freopen("sail.in", "r", stdin);
freopen("sail.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--){
cin >> n >> k;
cin >> s;
int cnt0 = 0, cnt1 = 0;
for (int i = n - 1; i >= 0; i--){
if (s[i] == '0')
cnt0++;
if (s[i] == '1')
cnt1++;
w[i] = cnt1 - cnt0;
}
sort(w + 1, w + n);
long long sum = 0, m = 1;
bool flag = 1;
for (int i = n - 1; i > 0; i--){
if (w[i] <= 0){
flag = 0;
break;
}
sum += w[i];
m++;
if (sum >= k)
break;
}
if (!flag || sum < k)
cout << -1 << '\n';
else
cout << m << '\n';
}
}
B.tree
原题链接:树上旅行(tree)
分析
呃,等比数列+快速幂+找规律——我没切就是橙题!我依旧认为这题比$A$简单。
好,我们学习一下狼人嚎的$blog$。
考虑向下跳
当前点为$pos$,其第$x$个儿子下标为$pos\times n+1+x - n$,向该方向走$y$步会走到$pos\times ny+\sum\limits_{i=0}(1+x)\times n^i=pos\times n^y+(1+x-n)\times \sum\limits_{i=0}{y}ny$。
可以预处理$\sum\limits_{i = 0}^{y}n\times i$,也可以写等比数列……
考虑向上跳
把向下跳压入栈中,每次弹栈。若$y$大于等于上次操作步数($top_y$)
则$pos=pos-(1+top_x-n)\times \sum\limits_{i = 0}{top_y}n$
然后$pos=\frac{pos}{n^{top_y}}$
当然要求$n^{top_y}$的逆元。
最后$y=y-top_y$。
弹栈即可,直到找不到 $y<top_y$
正解
#include <bits/stdc++.h>
#define mod 998244353
using namespace std;
const int N = 100005;
int n, q;
stack<pair<int, int>> stk;
long long base[N], s[N], pos = 1;
long long mpow(long long x, long long y){
if (y == 0)
return 1;
if (y & 1)
return mpow(x * x % mod, y / 2) * x % mod;
return mpow(x * x % mod, y / 2);
}
int main(){
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
base[0] = 1;
for (int i = 1; i < N; i++)
base[i] = base[i - 1] * n % mod;
s[1] = 1;
for (int i = 2; i < N; i++)
s[i] = (s[i - 1] + base[i - 1]) % mod;
while (q--){
int x, y;
cin >> x >> y;
if (x != 0){
pos = ((pos * base[y] % mod + (1 + x - n) * s[y] % mod) % mod + mod) % mod;
stk.push({x, y});
}
else{
while (stk.size() && y >= stk.top().second){
pos = (pos - (1 + stk.top().first - n) * s[stk.top().second] % mod + mod) % mod;
pos = pos * mpow(base[stk.top().second], mod - 2) % mod;
y -= stk.top().second;
stk.pop();
}
if (y){
pos = (pos - (1 + stk.top().first - n) * s[y] % mod + mod) % mod;
pos = pos * mpow(base[y], mod - 2) % mod;
pair<int, int> tmp = stk.top();
tmp.second -= y;
stk.pop();
stk.push(tmp);
}
}
cout << pos << '\n';
}
}
D.peach
提交链接:蟠桃(peach)
先补$T4$,据说更简单。
分析
有向图,看似是神秘连通性问题+计数。
30分
$O(n2)$枚举$l$,$r$,$O(n)$$check$,$O(n3)$,非常划算。
45分
记录$i$出边及自己的最小下标和最大下标。如果一个区间包含下标$i$,则必然包含$[mn_i,mx_i]$。
枚举$mn$,然后在枚举$mx$的过程中动态维护$\min mn_i,\max mx_i$。复杂度$O(n^2)$。
有部分分的STD,好评!
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5, M = 2e6 + 5;
int c, n, m, x, y, l[M], r[M];
long long ans;
signed main(){
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
freopen("peach.in", "r", stdin);
freopen("peach.out", "w", stdout);
cin >> c >> n >> m;
for(int i = 1; i <= n; i++)
l[i] = r[i] = i;
for(int i = 1; i <= m; i++){
cin >> x >> y;
l[x] = min(l[x], y);
r[x] = max(r[x], y);
}
for(int i = 1; i <= n; i++){
int ll = n, rr = 0;
for(int j = i; j <= n; j++){
ll = min(ll, l[j]);
rr = max(rr, r[j]);
if(ll < i) break;
if(rr > j) continue;
ans++;
}
}
cout << ans;
}
100分
呃……并非很懂,但是,思想就是尝试固定左端点或右端点,找另一个性质。
那么,合法区间一定满足$mn_l=l,mx_r=r$。
然后……我也不知道了,粘一下$STD$。
STD
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5, M = 2e6 + 5;
int c, n, m, x, y, l[M], r[M], lt[M], lg[N];
long long ans;
int head[M], tot, fl[N][21], fr[N][21];
bool inst[M], vis[M];
stack<int> stk;
struct edge
{
int to, nxt;
} e[M << 2];
void addedge(int u, int v)
{
e[++tot] = {v, head[u]};
head[u] = tot;
}
int calc_fl(int l, int r)
{
int x = lg[r - l + 1];
return min(fl[l][x], fl[r - (1 << x) + 1][x]);
}
int calc_fr(int l, int r)
{
int x = lg[r - l + 1];
return max(fr[l][x], fr[r - (1 << x) + 1][x]);
}
int stk2[M], top;
signed main()
{
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
freopen("peach.in", "r", stdin);
freopen("peach.out", "w", stdout);
cin >> c >> n >> m;
for(int i = 1; i <= n; i++) l[i] = r[i] = i;
for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= m; i++)
{
cin >> x >> y;
l[x] = min(l[x], y);
r[x] = max(r[x], y);
}
for(int i = 1; i <= n; i++)
fl[i][0] = l[i], fr[i][0] = r[i];
for(int j = 1; j <= 20; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
{
fl[i][j] = min(fl[i][j - 1], fl[i + (1 << (j - 1))][j - 1]);
fr[i][j] = max(fr[i][j - 1], fr[i + (1 << (j - 1))][j - 1]);
}
for(int i = 1; i <= n; i++)
{
while(top && stk2[top] > l[i]) top--;
if(l[i] == i) stk2[++top] = l[i];
if(i != r[i]) continue;
int L = 1, R = i, p = i;
while(L <= R)
{
int mid = (L + R) >> 1;
if(calc_fr(mid, i) <= i) p = mid, R = mid - 1;
else L = mid + 1;
}
L = 1, R = top; int q = top + 1;
while(L <= R)
{
int mid = (L + R) >> 1;
if(stk2[mid] >= p) q = mid, R = mid - 1;
else L = mid + 1;
}
ans += top - q + 1;
}
cout << ans;
return 0;
}
C.npy
提交链接:好朋友(npy)
我认为这题并不难,至少我想到了部分性质——LIS和LDS恰有一个交点$a_n$。
那么,就是组合+拼好算,没了……
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244853, M = 1e6 + 5;
int n, a[M * 2], f[M], g[M], b[M], fac[M], inv[M], ls, h[M][2], s[M][2];
inline int ksm(int x, int y)
{
int res = 1;
while(y)
{
if(y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod, y >>= 1;
}
return res;
}
int C(int n, int m)
{
if(n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int calc(int ls, int p, int x)
{
int res = 0;
for(int o = 0; o <= 1; o++)
{
if(h[ls][o] == -1) continue;
res = (res + 1ll * s[ls][o] * C(p - ls, x - h[ls][o]) % mod) % mod;
}
return res;
}
void solve()
{
cin >> n;
for(int i = 1; i <= 2 * n - 1; i++) cin >> a[i];
for(int i = 1; i <= n; i++) b[i] = a[2 * n - i];
for(int j = 0; j <= n; j++) f[j] = g[j] = 0;
for(int i = 1; i <= n; i++) h[i][0] = h[i][1] = -1, s[i][0] = s[i][1] = 0;
h[0][0] = 0, s[0][0] = 1, ls = 0;
for(int i = 1; i <= n - 1; i++)
{
if(a[i])
{
if(a[i] > 0)
{
int s1 = calc(ls, i - 1, a[i] - 1); // ls 贡献到 f[i - 1][a[i] - 1];
if(s1) h[i][0] = a[i], s[i][0] = s1;
}
if(i >= n - a[i] + 1)
{
if(i - (n - a[i] + 1) < i)
{
int s2 = calc(ls, i - 1, i - (n - a[i] + 1));
if(h[i][0] != -1 && h[i][0] != i - (n - a[i] + 1) && s[i][0] != 0) h[i][1] = i - (n - a[i] + 1), s[i][1] = s2;
else h[i][0] = i - (n - a[i] + 1), s[i][0] += s2, s[i][0] %= mod;
}
}
ls = i;
}
}
for(int i = 0; i <= n - 1; i++) f[i] = calc(ls, n - 1, i);
for(int i = 1; i <= n; i++) h[i][0] = h[i][1] = -1, s[i][0] = s[i][1] = 0;
h[0][0] = 0, s[0][0] = 1, ls = 0;
for(int i = 1; i <= n - 1; i++)
{
if(b[i])
{
if(b[i] > 0)
{
int s1 = calc(ls, i - 1, b[i] - 1); // ls 贡献到 g[i - 1][b[i] - 1];
if(s1) h[i][0] = b[i], s[i][0] = s1;
}
if(i >= n - b[i] + 1)
{
if(i - (n - b[i] + 1) < i)
{
int s2 = calc(ls, i - 1, i - (n - b[i] + 1));
if(h[i][0] != -1 && h[i][0] != i - (n - b[i] + 1) && s[i][0] != 0) h[i][1] = i - (n - b[i] + 1), s[i][1] = s2;
else h[i][0] = i - (n - b[i] + 1), s[i][0] += s2, s[i][0] %= mod;
}
}
ls = i;
}
}
int ans = 0;
for(int i = 0; i <= n - 1; i++) g[i] = calc(ls, n - 1, i);
if(a[n]) cout << 1ll * f[a[n] - 1] * g[a[n] - 1] % mod << '\n';
else
{
for(int i = 0; i <= n - 1; i++)
ans += 1ll * f[i] * g[i] % mod, ans %= mod;
cout << ans << '\n';
}
return;
}
int main(){
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
freopen("npy.in", "r", stdin);
freopen("npy.out", "w", stdout);
fac[0] = 1;
for(int i = 1; i <= 1e6; i++)
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[(int) 1e6] = ksm(fac[(int) 1e6], mod - 2);
for(int i = 1e6; i >= 1; i--)
inv[i - 1] = 1ll * inv[i] * i % mod;
int t; cin >> t;
while(t--) solve();
return 0;
}
不明白可以看这篇。
ZYZ28-NOIP-Xiaomao Round 1 题解 - 心灵震荡 - 博客园
密码:$xiao-mao-xi-huan-mo-ni-248224822482$
posted on 2025-11-19 17:11 hetao1733837 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号