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,k−1,i=0max2k−1dpx+2k−1,y+i,k−1)
转移时按
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=0max2k−1dpx+2k−1,y+i,k−1 一项可以用单调队列优化掉复杂度,总体复杂度
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;
}