2025 暑假集训 Day11

NOIP 模拟赛,难度蓝蓝紫黑,但是 T3数据贼水导致各种乱搞过,所以也算大水题。赛时还是完美避开 T3 大水题导致喜提 \(0+40+21+10=71pts\) 抽象分数和 \(26/44\) 抽象排名。

A. 餐馆

题目

题目描述:K 妹的胡椒粉大卖,这辣味让食客们感到刺激,许多餐馆也买这位 K 妹的账。有 \(N\) 家餐馆,有 \(N-1\) 条道路,这 \(N\) 家餐馆能相互到达。K 妹从 \(1\) 号餐馆开始。每一个单位时间,K 妹可以在所在餐馆卖完尽量多的胡椒粉,或者移动到有道路直接相连的隔壁餐馆。第 \(i\) 家餐馆最多需要 \(A_{i}\) 瓶胡椒粉。K 妹有 \(M\) 个单位的时间,问她最多能卖多少胡椒粉。

输入格式:第一行有两个正整数 \(N\)\(M\)。第二行描述餐馆对胡椒粉的最大需求量,有 \(N\) 个正整数,表示 \(A_{i}\)。接下来有 \(N-1\) 行描述道路的情况,每行两个正整数 \(u\)\(v\),描述这条道路连接的两个餐馆。

输出格式:一个整数,表示她最多能卖的胡椒粉瓶数。

样例:

【样例1输入】
3 5
9 2 5
1 2
1 3
【样例1输出】
14
【样例1说明】
在样例1中,K妹到达城市2后就恰好没时间卖辣椒粉了。

【样例2输入】
4 5
1 1 1 2
1 2
2 3
3 4
【样例2输出】
3

【样例3输入】
5 10
1 3 5 2 4
5 2
3 1
2 3
4 2
【样例3输出】
15

数据范围:对于 \(10\%\) 的数据,\(N \leq 20\)。对于 \(50\%\) 的数据,\(N \leq 110\)。对于 \(100\%\) 的数据,\(1 \leq N, M \leq 500\)\(1 \leq A_{i} \leq 10^6\)

\(5\) 到第 \(10\) 个测试点都有多个子测试。

树形 DP,设 \(dp(u,t,0/1)\) 表示以 \(u\) 为根的子树中,花费 \(t\) 单位时间,最终不回到/回到 \(u\) 的收益最大值。

B. string

题目 题目描述:给定一个由小写字母组成的字符串 $s$。有 $m$ 次操作,每次操作给定 $3$ 个参数 $l, r, x$。如果 $x=1$,将 $s[l] \sim s[r]$ 升序排序;如果 $x=0$,将 $s[l] \sim s[r]$ 降序排序。你需要求出最终序列。

输入格式:第一行两个整数 \(n, m\)。第二行一个字符串 \(s\)。接下来 \(m\) 行每行三个整数 \(l, r, x\)

输出格式:一行一个字符串表示答案。

样例:

【样例输入】
5 2
cabcd
1 3 1
3 5 0
【样例输出】
abdcc

数据范围:对于 \(40\%\) 的数据,\(n, m \leq 1000\)。对于 \(100\%\) 的数据,\(n, m \leq 100000\)

原题:CF558E A Simple Task

首先可以考虑使用计数排序,对于每一次修改的区间 \([l,r]\) 开一个桶 cnt 统计一下 a~z 出现的次数,然后如果是升序排序的话就把 a~z 按顺序丢回数组里面,降序 z~a 就可以了。时间复杂度是 \(O(nm)\) 的。

然后就有了以下的 AC 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e5+7;
int n,m,cnt[200];
char s[N];
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	int n,m,l,r,op;
	scanf("%d%d%s",&n,&m,(s+1));
	while(m--)
	{
		scanf("%d%d%d",&l,&r,&op);
		for(int c='a';c<='z';c++) cnt[c]=0;
		for(int i=l;i<=r;i++) cnt[(int)s[i]]++;
		if(op==1) //升序 a~z
		{
			int idx=l;
			for(int c='a';c<='z';c++) while(cnt[c]>0) s[idx++]=c,cnt[c]--;
		}
		else  //降序 z~a 
		{
			int idx=l;
			for(int c='z';c>='a';c--) while(cnt[c]>0) s[idx++]=c,cnt[c]--;
		}
	}
	puts(s+1);
	return 0;
}

没错,这一题时限 3000ms,我的这个代码跑了 2917ms 成功卡了过,应该是数据太水了。

考虑怎么优化。发现我们花了太多时间(\(O(n)\))统计 s[i] 中出现的字母个数。考虑开 26 个线段树维护区间 \([x,y]\) 中字母 a~z 出现的次数,在最后一起算一下就好了。

C. 购物

题目 X 城的商场中,有着琳琅满目的各种商品。一日,小X 带着小Y 前来购物,小Y 一共看中了n件商品,每一件商品价格为Pi。小X 现在手中共有m个单位的现金,以及k 张优惠券。小X 可以在购买某件商品时,使用至多一张优惠券,若如此做,该商品的价格会下降至Qi。

小X 希望尽可能多地满足小Y 的愿望,所以小X 想要知道他至多能购买多少件商品。

原题:P3045 [USACO12FEB] Cow Coupons G

有一个假的做法:首先选出 \(k\)\(q\) 最小的商品,先买了,然后再把剩下的商品按照 \(p\) 排序一遍,把剩下的钱花完,最后输出答案就好了。这个做法在学校 OJ 和洛谷原数据是能过的,但是过不了 Hack 数据。

先贴一个假的做法代码在这,下次再来搞:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=5e4+7;
int n,k;
ll m;
struct node{ll p,q;}a[N];
main()
{
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	cin>>n>>k>>m;
	for(int i=1;i<=n;i++) cin>>a[i].p>>a[i].q;
	sort(a+1,a+n+1,[&](node A,node B){return A.q<B.q;});
	int ans=0;
	for(int i=1;i<=k;i++)
	{
		if(m>=a[i].q)
		{
			m-=a[i].q;
			a[i].p=a[i].q=1e18;
			ans++;
		}
		else
		{
			cout<<ans;
			exit(0);
		}
	}
	sort(a+1,a+n+1,[&](node A,node B){return A.p<B.p;});
	for(int i=1;i<=n;i++)
	{
		if(m>=a[i].p)
		{
			m-=a[i].p;
			ans++;
		}
		else
		{
			cout<<ans;
			exit(0);
		}
	}
	cout<<ans;
	return 0;
}

D. 相互再归的鹅妈妈

题目:给定一个长度为 \(m\) 的二进制数 \(R\),求有多少种方式选择恰好 \(n\)\([0,R-1)\) 内的不同的数使得它们的异或和为 \(0\)

\(n \leq 7\)\(m \leq 5 \times 10^5\)

防 AK 题,而且还防赛后改题 AK 的。我不会,长大以后再学习!

posted @ 2025-08-15 19:57  wwwidk1234  阅读(31)  评论(0)    收藏  举报