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)    收藏  举报

导航