abc339 E - Smooth Subsequence
思路:
我们很容想到一个
n
n
n方的的状态转移方程,即对于每个i,我们去枚举
1
1
1到
i
−
1
i-1
i−1的状态,即
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
;
dp[i]=max(dp[i],dp[j]+1);
dp[i]=max(dp[i],dp[j]+1);
但是由于时间的限制,我们无法使用n方的dp去完成这道题目,考虑进行优化,因为对于状态
i
i
i,其状态只能由前面点的状态转移过来,我们同样可以换一种说法,对于值
a
[
i
]
a[i]
a[i],其状态只能由
(
a
[
i
]
−
d
,
a
[
i
]
+
d
)
(a[i]-d,a[i]+d)
(a[i]−d,a[i]+d)这个范围内的数转移过来,我们想要长度最大,那么我们把值域当作下标进行维护,答案即为范围内的最大值,计算完当前状态后,我们再把当前的状态放入所谓的值域中。
对于上述操作,将值域看作下标后,其本质就是区间查询最大值,然后单点进行修改,因此线段树完全可以代替第二层循环,将时间复杂度降低到log级别。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 5> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"
int dp[N];
int a[N];
struct node
{
int l,r,val;
#define l(x) tr[x].l
#define r(x) tr[x].r
#define val(x) tr[x].val
}tr[N*4];
void update(int p)
{
val(p)=max(val(p*2),val(p*2+1));
}
void build(int p,int l,int r)
{
if(l==r){
tr[p]={l,r,0};
return ;
}
l(p)=l,r(p)=r;
int mid=(l+r)/2;
build(p*2,l,mid),build(p*2+1,mid+1,r);
}
void change(int p,int pos,int x)
{
if(l(p)==r(p)){
val(p)=x;
return ;
}
int mid=(l(p)+r(p))/2;
if(pos<=mid) change(p*2,pos,x);
else change(p*2+1,pos,x);
update(p);
}
int query(int p,int l,int r)
{
if(l<=l(p)&&r(p)<=r) return val(p);
int mid=(l(p)+r(p))/2;
int res=0;
if(l<=mid) res=max(res,query(p*2,l,r));
if(r>mid) res=max(res,query(p*2+1,l,r));
return res;
}
void solve()
{
int n,d;
cin>>n>>d;
for(int i=1;i<=n;i++){
cin>>a[i];
// dp[i]=1;
}
build(1,1,N);
for(int i=1;i<=n;i++){
int cur=query(1,max(0,a[i]-d),min(N,a[i]+d));
dp[i]=cur+1;
change(1,a[i],dp[i]);
}
cout<<query(1,1,N)<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
// cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
abc353 G - Merchant Takahashi
思路:
和上题一样,很容易想到一个
n
2
n^2
n2的dp转移,即
d
p
[
i
]
=
max
j
=
1
j
=
i
−
1
(
d
p
[
j
]
−
c
∣
t
i
−
t
j
∣
+
p
i
)
dp[i]=\max_{j=1}^{j=i-1} (dp[j]-c|t_i-t_j|+p_i)
dp[i]=j=1maxj=i−1(dp[j]−c∣ti−tj∣+pi)
那么考虑如何进行优化,对于递推方程,我们唯一难处理的地方就是绝对值,所以我们把绝对值拆开,进行分类讨论即可,那么上述式子则变成了
d
p
[
i
]
=
max
j
=
1
j
=
i
−
1
(
d
p
[
j
]
+
c
×
t
j
)
−
c
×
t
i
+
p
i
dp[i]=\max_{j=1}^{j=i-1}(dp[j]+c\times t_j)-c\times t_i+p_i
dp[i]=j=1maxj=i−1(dp[j]+c×tj)−c×ti+pi
d
p
[
i
]
=
max
j
=
1
j
=
i
−
1
(
d
p
[
j
]
−
c
×
t
j
)
+
c
×
t
i
+
p
i
dp[i]=\max_{j=1}^{j=i-1}(dp[j]-c\times t_j)+c\times t_i+p_i
dp[i]=j=1maxj=i−1(dp[j]−c×tj)+c×ti+pi
对于
m
a
x
max
max部分都是关于
j
j
j的变量,说的更具象一点,则是对于当前位置
t
i
t_i
ti,只能从
t
j
t_j
tj转移而来,所以我们维护每个
t
j
t_j
tj对应位置的值即可,那么则变为了查询区间最大值,使用线段树维护就行。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> ar;
const int mod = 1e9+7;
const int maxv = 4e6 + 5;
// #define endl "\n"
template<const int T>
struct ModInt {
const static int mod = T;
int x;
ModInt(int x = 0) : x(x % mod) {}
ModInt(long long x) : x(int(x % mod)) {}
int val() { return x; }
ModInt operator + (const ModInt &a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
ModInt operator - (const ModInt &a) const { int x0 = x - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
bool operator == (const ModInt &a) const { return x == a.x; };
bool operator != (const ModInt &a) const { return x != a.x; };
void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; }
void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; }
void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
void operator /= (const ModInt &a) { *this = *this / a; }
friend ModInt operator + (int y, const ModInt &a){ int x0 = y + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
friend ModInt operator - (int y, const ModInt &a){ int x0 = y - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
friend ModInt operator * (int y, const ModInt &a){ return ModInt(1LL * y * a.x % mod);}
friend ModInt operator / (int y, const ModInt &a){ return ModInt(y) / a;}
friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
friend istream &operator>>(istream &is, ModInt &t){return is >> t.x;}
ModInt pow(int64_t n) const {
ModInt res(1), mul(x);
while(n){
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
ModInt inv() const {
int a = x, b = mod, u = 1, v = 0;
while (b) {
int t = a / b;
a -= t * b; swap(a, b);
u -= t * v; swap(u, v);
}
if (u < 0) u += mod;
return u;
}
};
using mint = ModInt<998244353>;
ll a[N];
struct segment_tree
{
struct node
{
ll l,r,val;
#define l(x) tr[x].l
#define r(x) tr[x].r
#define val(x) tr[x].val
}tr[N*4];
void update(int p)
{
val(p)=max(val(p*2),val(p*2+1));
}
void build(int p,int l,int r)
{
if(l==r){
tr[p]={l,r,a[l]};
return ;
}
l(p)=l,r(p)=r;
int mid=(l+r)/2;
build(p*2,l,mid),build(p*2+1,mid+1,r);
update(p);
}
void change(int p,int pos,ll x)
{
if(l(p)==r(p)){
val(p)=x;
return ;
}
int mid=(l(p)+r(p))/2;
if(pos<=mid) change(p*2,pos,x);
else change(p*2+1,pos,x);
update(p);
}
ll query(int p,int l,int r)
{
if(l<=l(p)&&r(p)<=r) return val(p);
int mid=(l(p)+r(p))/2;
ll res=-1e18;
if(l<=mid) res=max(res,query(p*2,l,r));
if(r>mid) res=max(res,query(p*2+1,l,r));
return res;
}
};
segment_tree t1,t2;
void solve()
{
ll n,c;
int m;
cin>>n>>c;
cin>>m;
vector<pll> inf(m+5);
for(int i=1;i<=m;i++){
ll t,p;
cin>>t>>p;
inf[i]={t,p};
}
a[1]=0;
for(int i=2;i<=n;i++) a[i]=-1e18;
t1.build(1,1,n);
t2.build(1,1,n);
ll ans=0;
for(int i=1;i<=m;i++){
auto &[t,p]=inf[i];
int id=t;
t--;
ll dp1=t1.query(1,1,id)-c*t;//因为是分类讨论,当ti>tj时,对应的tj范围是1到ti;
ll dp2=t2.query(1,id,n)+c*t;//同理
ll dp=max(dp1,dp2)+p;//计算出当前点的最大值
ans=max(ans,dp);
t1.change(1,id,dp+1ll*c*t);//分别进行更新
t2.change(1,id,dp-1ll*t*c);
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t = 1;
// cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}