当前位置: 首页 > article >正文

xbh的比赛

T1

我们发现第一问很好解决,就直接按照从大到小的顺序排序,然后依次选就行了。
然后第一问的答案就出来了。
随后我们看第二问,我们发现和零一背包很相似。
解决这样一个问题, 我们可以使用 01 背包。用 f i , j , k f_{i, j, k} fi,j,k 表示在前 i 个瓶子中选择了 j 个瓶子, 总容积为 k 的最大不移动的液体体积和。我们可以得到:

f i , j , k = max ⁡ ( f i − 1 , j , k , f i − 1 , j − 1 , k − b i + a i ) f_{i, j, k}=\max \left(f_{i-1, j, k}, f_{i-1, j-1, k-b_{i}}+a_{i}\right) fi,j,k=max(fi1,j,k,fi1,j1,kbi+ai)

然后我们用滚动数组优化,把i这维省去就可以了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0') {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=110;
struct node {
	int a,b;
} e[N];
int sum[N];
int all;
bool cmp(node x,node y) {
//	if(x.b==y.b) return x.a>y.a;
	return x.b>y.b;
}
int f[N][10010];
signed main() {
//	freopen("3.in","r",stdin);
	int n=read();
	for(int i=1; i<=n; i++) {
		e[i].a=read();
		all+=e[i].a;
	}
	for(int i=1; i<=n; i++) {
		e[i].b=read();
//		sum[i]=sum[i-1]+e[i].b;
	}
	sort(e+1,e+1+n,cmp);
	int id=0;
	for(int i=1; i<=n; i++) {
		sum[i]=sum[i-1]+e[i].b;
		if(sum[i]>=all) {
			id=i;
			break;
		}
	}
	int res=0,ans=0;
	for(int i=1;i<=id;i++){
		res+=e[i].b;
	}
//	cout<<res<<endl;
	memset(f,-0x3f,sizeof(f));
	f[0][0]=0;
	for(int i=1; i<=n; i++){
		for(int j=id; j; j--){
			for(int k=res; k>=e[i].b; k--){
				f[j][k]=max(f[j][k],f[j-1][k-e[i].b]+e[i].a);
			}
		}
	}
//	cout<<all<<"  "<<res<<endl;
	for(int i=all; i<=res; i++){
//		cout<<f[id][i]<<endl;
		ans=max(ans,f[id][i]);
	} 
//	cout<<"OP"<<sum;
	printf("%lld %lld",id,all-ans);
	return 0;
}

T2

这道题目看到数学式子不能硬求,一般遇到都要进行化简。
我们进行化简: ∑ i = 1 n ( x i − x ˉ ) 2 n = ∑ i = 1 n x i 2 n − ( x ˉ ) 2 \frac{\sum_{i=1}^{n}\left(x_{i}-\bar{x}\right)^{2}}{n}=\frac{\sum_{i=1}^{n} x_{i}^{2}}{n}-(\bar{x})^{2} ni=1n(xixˉ)2=ni=1nxi2(xˉ)2 。显然对于每个询问, 平均数是固定的, 所以题目相当于最小化平方和。假设一开始所有 a i 都取到 l i a_{i} 都取到 l_{i} ai都取到li , 只需要以此为基础将一些 a i a_{i} ai 的值增加。若将 x − 1 x-1 x1 增加到 x x x , 平方和增加 x 2 − ( x − 1 ) 2 = x × 2 − 1 x^{2}-(x-1)^{2}=x \times 2-1 x2(x1)2=x×21 。所以显然每次将最小值增加 1 1 1 最优。
然后我们就是实现了:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+26,mx=1e6,mod=998244353;
struct Query {
	ll x,id,ans;
	friend bool operator<(Query xy,Query zb) {
		return xy.x<zb.x;
	}
} qq[maxn];
ll n,q,invn,l[maxn],r[maxn],cnt[maxn],ans,ansx;
ll read() {
	ll x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0') {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
inline ll Pow(ll x,ll y) {
	if(!y)
		return 1;
	if(y&1)
		return Pow(x,y^1)*x%mod;
	return Pow(x*x%mod,y>>1);
}
int main() {
	n=read(),q=read();
	invn=Pow(n,mod-2);
	for(ll i=1; i<=n; i++) {
		l[i]=read(),r[i]=read();
		ans+=l[i]*l[i];
		ansx+=l[i];
		cnt[l[i]+1]++;
		cnt[r[i]+1]--;
	}
	for(ll i=0; i<=mx; i++)
		cnt[i]+=cnt[i-1];
	for(ll i=1; i<=q; i++) {
		qq[i].x=read();
		qq[i].id=i;
	}
	sort(qq+1,qq+q+1);
	for(ll i=1,j=0; i<=q; i++) {
		while(ansx<qq[i].x) {
			if(qq[i].x-ansx<cnt[j]) {
				cnt[j]-=(qq[i].x-ansx);
				(ans+=(qq[i].x-ansx)*(j*2-1)%mod)%=mod;
				ansx=qq[i].x;
				break;
			}
			ansx+=cnt[j];
			(ans+=cnt[j]*(j*2-1)%mod)%=mod;
			j++;
		}
		qq[i].ans=ans;
		qq[i].x%=mod;
	}
	sort(qq+1,qq+q+1,[](Query xy,Query zb) {return xy.id<zb.id;});
	for(ll i=1; i<=q; i++)
		printf("%lld\n",(qq[i].ans*invn%mod-qq[i].x*qq[i].x%mod*invn%mod*invn%mod+mod)%mod);
	return 0;
}

T3

这道题感觉和滑动窗口很像,然后我们往单调队列想,就不难得出正解。
我们用单调队列来维护给定区间的最大值。
f i , j f_{i,j} fi,j表示,前i个数已经选了j个数的最大值,单调队列里维护的是最大值点的位置,对转移进行优化,省去了不必要的转移

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0') {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}

const int N=5050;
int f[N][N];
int q[N];
int a[N];
int ans;
signed main(){
//	freopen("3.in","r",stdin);
	memset(f,0xcf,sizeof f);
	int n=read(),k=read(),x=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	if(n/k>x){
		puts("-1");
		return 0;
	}
	f[0][0]=0;
	for(int j=1;j<=x;j++){
		int l=1,r=1;
		q[r]=0;
		for(int i=1;i<=n;i++){
			while(l<=r&&q[l]+k<i) l++;
			f[i][j]=f[q[l]][j-1]+a[i];
			while(l<=r&&f[i][j-1]>=f[q[r]][j-1]) r--;
			q[++r]=i;
		}
	}
	for(int i=n-k+1;i<=n;i++){
		ans=max(ans,f[i][x]);
	}
	printf("%lld",ans);
	return 0;
}

T4

说来也奇怪,我的贪心跑出来的最小值比答案的最小值还小,得分 17 p t s 17pts 17pts
先说一下我的假做法。
我们可以按高度进行排序,然后我们就依次进行判断就行了,这样肯定是假的,但是在写不出正解的情况下就得写一个这个。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=1e5+10;
struct node{
	int h,w; 
}gf[N];//女朋友 
bool cmp(node a,node b){
	return a.h>b.h;
}
signed main(){
//	freopen("1.in","r",stdin);
	int n=read(),l=read();
	for(int i=1;i<=n;i++){
		gf[i].h=read(),gf[i].w=read();
	}
	sort(gf+1,gf+1+n,cmp);
	int ans=0,res=0;
	int maxn=0;
	for(int i=1;i<=n;i++){
		if(res+gf[i].w>l){
			res=0;
//			cout<<maxn<<endl;
			ans+=maxn;
			maxn=0;
		}
		res+=gf[i].w;
		maxn=max(maxn,gf[i].h);
	}
	ans+=maxn;
	printf("%lld",ans);
	return 0;
}

然后考虑正解
用线段树优化DP
f i f_{i} fi 表示第 i i i 本书结尾时高度和的最小值。

f i = min ⁡ ( f j + max ⁡ ( h j + 1 , … , h i ) ) ( w j + 1 + … + w i ≤ L ) f_{i}=\min \left(f_{j}+\max \left(h_{j+1}, \ldots, h_{i}\right)\right)\left(w_{j+1}+\ldots+w_{i} \leq L\right) fi=min(fj+max(hj+1,,hi))(wj+1++wiL)

w w w 这个限制我们可以通过二分得到
现在我们把问题转化成:

f i = min ⁡ ( f j + max ⁡ ( h j + 1 , … , h i ) ) ( pos ⁡ i ≤ j ) f_{i}=\min \left(f_{j}+\max \left(h_{j+1}, \ldots, h_{i}\right)\right)\left(\operatorname{pos}_{i} \leq j\right) fi=min(fj+max(hj+1,,hi))(posij)

其中 p o s i p o s_{i} posi 为第 i 个位置的最左端点。

然后是如何使用线段树优化
p o s i pos_i posi的值我们可以在一开始用双指针把区间存起来
而且我们还能用线段树二分来找到 p o s i pos_i posi 这个值,但设个我感觉就复杂了
首先, 我们先开一个单调栈, 对于每个位置找出第一个大于 h i h_{i} hi 的位置 l , 并将 [ l + 1 , i ] [l+1, i] [l+1,i] 这段区间内的 h h h 赋为 h i h_{i} hi 。单调栈可以直接找到需要更新区间的端点

我们发现每一个位置的 f f f 是确定的, 我们每次只需区间修改 h h h 的值, 然后维护 f + h f+h f+h f f f 的最小值。
最后更新用区间查询就行了

#include <bits/stdc++.h>
#define ll long long
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int maxn=100000+10;
const ll inf=1e18;
int n,L,w[maxn],h[maxn],pre[maxn],sta[maxn],top;
ll sum[maxn],f[maxn],Min[maxn<<2],val[maxn<<2],tag[maxn<<2];
// Min 为 f + h 的最小值
// val 为 f 的最小值

inline int read() {
	register int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)) {
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)) {
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return (f==1)?x:-x;
}

inline void pushup(int rt) {
	Min[rt]=min(Min[lson],Min[rson]);
	val[rt]=min(val[lson],val[rson]);
}

inline void pushdown(int rt) {
	if(tag[rt]!=inf) {
		Min[lson]=val[lson]+tag[rt];
		Min[rson]=val[rson]+tag[rt];
		tag[lson]=tag[rson]=tag[rt];
		tag[rt]=inf;
	}
}

void build(int l,int r,int rt) {
	Min[rt]=val[rt]=tag[rt]=inf;
	if(l == r) return ;
	int mid=(l+r)>>1;
	build(l,mid,lson);
	build(mid+1,r,rson);
}

void update(int L,int R,int C,int l,int r,int rt) {
	if(L <= l && r <= R) {
		Min[rt]=val[rt]+C;
		tag[rt]=C;
		return ;
	}
	pushdown(rt);
	int mid=(l+r)>>1;
	if(L <= mid) update(L,R,C,l,mid,lson);
	if(R > mid) update(L,R,C,mid+1,r,rson);
	pushup(rt);
}

void modify(int x,int l,int r,int rt) {
	if(l == r) {
		Min[rt]=inf;
		val[rt]=f[l-1];
		return ;
	}
	pushdown(rt);
	int mid=(l+r)>>1;
	if(x <= mid) modify(x,l,mid,lson);
	else modify(x,mid+1,r,rson);
	pushup(rt);
}

ll query(int L,int R,int l,int r,int rt) {
	if(L <= l && r <= R) return Min[rt];
	pushdown(rt);
	int mid=(l+r)>>1;
	ll ans=inf;
	if(L <= mid) ans=min(ans,query(L,R,l,mid,lson));
	if(R > mid) ans=min(ans,query(L,R,mid+1,r,rson));
	return ans;
}

int main() {
	n=read(),L=read();
	for(int i=1; i<=n; i++) {
		h[i]=read(),w[i]=read();
		sum[i]=sum[i-1]+w[i];
	}
	sta[++top]=1;
	for(int i=2; i<=n; i++) {
		while(top&&h[i]>h[sta[top]]) top--;
		if(top) pre[i]=sta[top];
		sta[++top]=i;
	}
	build(1,n,1);
	for(int i=1; i<=n; i++) {
		modify(i,1,n,1);
		if(pre[i]+1<=i) update(pre[i]+1,i,h[i],1,n,1);
		int l=lower_bound(sum,sum+i+1,sum[i]-L)-sum;
		if(l<i) f[i]=query(l+1,i,1,n,1);
	}
	printf("%lld\n",f[n]);
	return 0;
}

这次比赛全是DP,差评完全不像模拟赛


http://www.kler.cn/a/405305.html

相关文章:

  • 如何进行Apache的配置与调试?
  • 经济增长初步
  • 图像增强夜视仪行业全面而深入的分析
  • sql注入报错分享(mssql+mysql)
  • Kafka Offset 自动提交和手动提交 - 漏消费与重复消费
  • Docker Registry(镜像仓库)详解
  • Qt 的事件投递机制:从基础到实战
  • 动态调试对安全研究有什么帮助?
  • 设计模式之 模板方法模式
  • vue中路由缓存
  • Python创建虚拟环境报错:Error: Command......
  • 项目中排查bug的思路案例
  • 【Spring MVC】关于Spring MVC编程中与http请求的参数传递的详细介绍
  • 【MySQL系列】深入理解MySQL中的存储、排序字符集
  • Ubuntu20.04从零安装IsaacSim/IsaacLab
  • python内存分析
  • Qt-常用的显示类控件
  • Zookeeper集群搭建Centos环境下
  • ROS机器视觉入门:从基础到人脸识别与目标检测
  • 网络安全中常用浏览器插件、拓展
  • 生日主题的烟花特效HTML,CSS,JS
  • 20241121 android中树结构列表(使用recyclerView实现)
  • 【K8S系列】imagePullSecrets配置正确,但docker pull仍然失败,进一步排查详细步骤
  • java: spire.pdf.free 9.12.3 create pdf
  • Android 应用添加系统签名权限介绍
  • reactflow 中 useOnViewportChange 模块作用