NOIP2023 T4 题解
用到的算法
- 离散化
- 线段树
- DP
思路与正解
看到这种题,莫名感觉要dp()
一个小结论
某一段打卡区间必然是从某一个挑战的起始日期开始,到某一挑战的结束日期为止
否则不是最优
这提示我们从挑战的开始与结束日期(事件天数)入手,设计dp
由于只关注开始与结束日期,中间相隔的大段时间没有用
故考虑把开始与结束日期离散化,用 t m p [ i ] tmp[i] tmp[i] 表示第 i i i个事件天数对应的正式日期
离散化的代码如下:
vector<int> tmp;
tmp.push_back(x), tmp.push_back(y);
sort(tmp.begin(),tmp.end());
tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
尝试搞一搞dp
状态设计
因为打卡区间与起始和结束日期相关,考虑如下设计状态
f [ i ] [ 0 ] f[i][0] f[i][0] 表示 第 i i i个事件天数作为一个打卡区间的开始的最大能量值
f [ i ] [ 1 ] f[i][1] f[i][1] 表示 第 i i i个事件天数作为一个打开区间的结束的最大能量值
怎么转移捏?
对于 f [ i ] [ 0 ] f[i][0] f[i][0]
f [ i ] [ 0 ] = m a x ( 0 , f [ 1 ] [ 1 ] , f [ 2 ] [ 1 ] , . . . . . . f [ i − 2 ] [ 1 ] ( , f [ i − 1 ] [ 1 ] ) ) f[i][0] = max(0,f[1][1],f[2][1],......f[i-2][1](,f[i-1][1])) f[i][0]=max(0,f[1][1],f[2][1],......f[i−2][1](,f[i−1][1]))
当 t m p [ i ] − t m p [ i − 1 ] = = 1 tmp[i]-tmp[i-1]==1 tmp[i]−tmp[i−1]==1,即两个事件天数是相邻的,由于两个打卡区间要有间断处,所以不算上 f [ i − 1 ] [ 1 ] f[i-1][1] f[i−1][1]这一项
否则就加上
m a x max max的操作可以用 p r i o r i t y _ q u e u e priority\_queue priority_queue维护
对于 f [ i ] [ 1 ] f[i][1] f[i][1]
考虑哪一个事件天数作为打卡区间的开始
由于题目 k k k的限制,计算出 m i n j minj minj 表示最小的与 i i i间隔不超过 k k k的事件天数,可以使用二分
f [ i ] [ 1 ] = m a x ( f [ j ] [ 0 ] + v a l [ j ] − c o s t [ j ] ) , j ∈ [ m i n j , i ] f[i][1] = max(f[j][0]+val[j]-cost[j]),j\in[minj,i] f[i][1]=max(f[j][0]+val[j]−cost[j]),j∈[minj,i]
v a l [ j ] val[j] val[j] 表示挑战落在事件天数 [ j , i ] [j,i] [j,i]内的能量值
c o s t [ j ] cost[j] cost[j]表示第 i ∼ j i\sim j i∼j天打卡消耗的能量值
这是很经典的可以用线段树优化的形式
具体地,令 t r [ i ] = f [ i ] [ 0 ] + v a l − c o s t tr[i] = f[i][0] + val - cost tr[i]=f[i][0]+val−cost
v a l val val与 c o s t cost cost的值伴随着计算的过程逐步修改
线段树维护的过程伪代码如下:
build();//建立一棵维护最大值得线段树 days为事件天数的总数
f[1][0] = 0;
for(i from 1 to days)
{
rangeadd(i,i,f[i][0]);//线段树区间加操作,range(ql,qr,val) 对从ql-qr的区间+val
对于每个结束日期为i的挑战://维护val
{
rangeadd(1,il,val);//il为起始日期对应的事件天数
}
计算代价:
{
rangeadd(1,i-1,-d*(tmp[i]-tmp[i-1]));
//对于第i天以前的事件天数,补上从tmp[i-1]+1~tmp[i]的能量消耗
rangeadd(i,i,-d);
//对于第i天,只需减掉当天的能量消耗即可
}
计算min_j
f[i][1] = query(min_j,i);//线段树查询操作,query(ql,qr) 从ql-qr区间查询
更新f[i+1][0]的值
}
ans = max(f[i][1]);//i from 1 to days
最大复杂度: 2 m l o g 2 m 2mlog2m 2mlog2m
code
#include<bits/stdc++.h>
#define N 200100
#define ll long long
#define k1 k<<1
#define k2 k<<1|1
using namespace std;
typedef struct
{
int l,r,il,ir,v;
}run;
bool cmp(const run &a, const run &b)
{
return a.ir < b.ir;
}
ll f[N][2];
ll tr[N<<2], lz[N<<2];
void build(int k,int l,int r)
{
if(l==r)
{
tr[k] = -1e18;
lz[k] = 0;
return;
}
int m = (l+r)>>1;
build(k1,l,m);
build(k2,m+1,r);
tr[k] = -1e18;
lz[k] = 0;
}
void update(int k)
{
tr[k] = max(tr[k1],tr[k2]);
}
void modify(int k,ll val)
{
tr[k] += val;
lz[k] += val;
}
void pushdown(int k)
{
modify(k1,lz[k]);
modify(k2,lz[k]);
lz[k] = 0;
}
ll query(int k,int l,int r,int ql,int qr)
{
if(ql<=l&&qr>=r) return tr[k];
if(lz[k]) pushdown(k);
int m = (l+r)>>1;
ll ret = -1e18;
if(ql<=m) ret = max(ret,query(k1,l,m,ql,qr));
if(qr>m) ret = max(ret,query(k2,m+1,r,ql,qr));
update(k);
return ret;
}
void pointmodify(int k,int l,int r,int p,ll val)
{
if(l==r)
{
tr[k] = val;
return;
}
if(lz[k]) pushdown(k);
int m = (l+r)>>1;
if(p<=m) pointmodify(k1,l,m,p,val);
else pointmodify(k2,m+1,r,p,val);
update(k);
}
void rangeadd(int k,int l,int r,int ql,int qr,ll val)
{
if(ql<=l&qr>=r)
{
modify(k,val);
return;
}
if(lz[k]) pushdown(k);
int m = (l+r)>>1;
if(ql<=m) rangeadd(k1,l,m,ql,qr,val);
if(qr>m) rangeadd(k2,m+1,r,ql,qr,val);
update(k);
}
void solve()
{
int n,m,k,d;
cin>>n>>m>>k>>d;
vector<run> r(m);
vector<int> tmp;
tmp.push_back(0);
for(int i = 0;i<m;i++)
{
int x,y,v;
cin>>x>>y>>v;
r[i].l = x-y+1, r[i].r = x, r[i].v = v;
tmp.push_back(x-y+1), tmp.push_back(x);
}
sort(tmp.begin(),tmp.end());
tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
int days = tmp.size()-1;
for(int i = 0;i<m;i++)
{
r[i].il = lower_bound(tmp.begin(),tmp.end(),r[i].l)-tmp.begin();
r[i].ir = lower_bound(tmp.begin(),tmp.end(),r[i].r)-tmp.begin();
}
sort(r.begin(),r.end(),cmp);
build(1,1,days);
f[1][0] = 0;
int index = 0;
priority_queue<ll> bg;
bg.push(0);
for(int i = 1;i<=days;i++)
{
pointmodify(1,1,days,i,f[i][0]);
while(index<m&&r[index].ir<i) index++;
while(index<m&&r[index].ir==i)
{
rangeadd(1,1,days,1,r[index].il,r[index].v);
index++;
}
if(i>1) rangeadd(1,1,days,1,i-1,-1ll*d*(tmp[i]-tmp[i-1]));
rangeadd(1,1,days,i,i,-d);
int j = upper_bound(tmp.begin(),tmp.end(),max(0,tmp[i]-k))-tmp.begin();//
f[i][1] = query(1,1,days,j,i);
if(tmp[i+1]-tmp[i]>1)
{
bg.push(f[i][1]);
f[i+1][0] = bg.top();
}
else
{
f[i+1][0] = bg.top();
bg.push(f[i][1]);
}
}
cout<<bg.top()<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int c,t;
cin>>c>>t;
while(t--)
{
solve();
}
}