“蔚来杯“2022牛客暑期多校训练营5 E题: Fraction Game

E题: Fraction Game

原题链接:https://ac.nowcoder.com/acm/contest/33190/E

题目大意

称高为 x x x 且第 i ( i ∈ [ 1 , x ] ) i(i\in [1,x]) i(i[1,x]) 行有 x x x 个格子的等腰三角形为大小为 x x x 的等腰三角形。

现有大小为 n n n 的等腰三角形,每个格子中有个分数。求其所有大小为 k k k 的等腰三角形中最大分数之和。

题解

可以用二维 S T ST ST 表预处理维护最大值,再进行查询。
d p x , y , k dp_{x,y,k} dpx,y,k 表示以第 x x x 行第 y y y 个格子为顶点的大小为 2 k 2^k 2k 的等腰三角形内的最大值。
考虑转移式:
d p x , y , k = max ⁡ ( d p x , y , k − 1 , max ⁡ i = 0 2 k − 1 d p x + 2 k − 1 , y + i , k − 1 ) dp_{x,y,k}=\max(dp_{x,y,k-1},\max_{i=0}^{2^{k-1}}dp_{x+2^{k-1},y+i,k-1}) dpx,y,k=max(dpx,y,k1,i=0max2k1dpx+2k1,y+i,k1)
转移时按 k , x , y k,x,y k,x,y 的顺序进行枚举,对于同一行的 d p dp dp 值, max ⁡ i = 0 2 k − 1 d p x + 2 k − 1 , y + i , k − 1 \max\limits_{i=0}^{2^{k-1}}dp_{x+2^{k-1},y+i,k-1} i=0max2k1dpx+2k1,y+i,k1 一项可以用单调队列优化掉复杂度,总体复杂度 O ( n 2 l o g k ) O(n^2logk) O(n2logk) ,可以通过此题,记得注意细节。

参考代码

#include<bits/stdc++.h>
using namespace std;

template<class T>inline void read(T&x){
	char c,last=' ';
	while(!isdigit(c=getchar()))last=c;
	x=c^48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
	if(last=='-')x=-x;
}

long long gcd(long long a,long long b){//最大公因数(约分用)
	return b?gcd(b,a%b):a;
}

long long lcm(long long a,long long b){//最小公倍数(约分用)
	return a/gcd(a,b)*b;
}

struct F{long long a,b;};//分数

bool operator==(const F&x,const F&y){
	return x.a==y.a&&x.b==y.b;
}

F max(const F&x,const F&y){
	if(x.a*y.b-y.a*x.b>0)return x;
	return y;
}

F operator+(const F&x,const F&y){//通分加法
	F ret;
	ret.b=lcm(x.b,y.b);
	ret.a=ret.b/x.b*x.a+ret.b/y.b*y.a;
	long long g=gcd(ret.a,ret.b);
	ret.a/=g,ret.b/=g;
	return ret;
}

const int MAXN=1e3+5;
int n,K;
int lg[MAXN];
F f[2][MAXN][MAXN];//ST表,使用滚动数组
int q[MAXN];
F ans;

int init(){
	for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
	int k=lg[K];//只需要处理到logK即可
	for(int t=1;t<=k;++t){
		int id=t&1;
		for(int i=1,I=1+(1<<t-1);i+(1<<t)-1<=n;++i,++I){
			int J,l=1,r=0;
			for(J=1;J<1+(1<<t-1);++J){
				while(l<=r&&q[l]<=J-(1+(1<<t-1)))++l;
				while(l<=r&&max(f[id^1][I][q[r]],f[id^1][I][J])==f[id^1][I][J])--r;
				q[++r]=J;
			}
			for(int j=1;j<=i;++j,++J){
				while(l<=r&&q[l]<=J-(1+(1<<t-1)))++l;
				while(l<=r&&max(f[id^1][I][q[r]],f[id^1][I][J])==f[id^1][I][J])--r;
				q[++r]=J;
				f[id][i][j]=max(f[id^1][I][q[l]],f[id^1][i][j]);
			}
		}
	}
	return k;
}

void solve(int k){
	int id=k&1;
	for(int i=1,I=K-(1<<k)+1;i+K-1<=n;++i,++I){
		int J,l=1,r=0;
		for(J=1;J<K-(1<<k)+1;++J){
			while(l<=r&&q[l]<=J-(K-(1<<k)+1))++l;
			while(l<=r&&max(f[id][I][q[r]],f[id][I][J])==f[id][I][J])--r;
			q[++r]=J;
		}
		for(int j=1;j<=i;++j,++J){
			while(l<=r&&q[l]<=J-(K-(1<<k)+1))++l;
			while(l<=r&&max(f[id][I][q[r]],f[id][I][J])==f[id][I][J])--r;
			q[++r]=J;
			ans=ans+max(f[id][I][q[l]],f[id][i][j]);
		}
	}
}

int main()
{
	read(n),read(K);ans.b=1;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=i;++j)read(f[0][i][j].a),read(f[0][i][j].b),f[0][i][j]=ans+f[0][i][j];//做一次加法(加上0/1)的目的是约分初始化
	}
	solve(init());
	cout<<ans.a<<'/'<<ans.b<<'\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值