《简单DP》
作者:互联网
简单DP
「JOISC 2020 Day4」治疗计划
首先考虑问题的维度\([治疗状态],[时间],[花费]\)
治疗状态肯定无法省去
时间其实可以忽略,因为最终的答案的可行性与时间无关,但可能会加大转移的难度
事实上时间是可以不入状态的
考虑用01串表示治疗状态,但其实可以直接用前缀治疗状态
设\(dp[i]\)表示\([1,i]\)已治好的最小花费
\(111100111\),类似的情况,因为是连续的区间,所以不会出现》
转换一下,设\(dp[i]\)表示第\(i\)个计划实施后\([1,r_i]\)已被治疗
\(dp[i]=dp[j]+c[i],(r_j-l_i+1\geq|t_i-t_j|)\)
解释一下,首先对于\(i\)找到一个\(j\)使得\([1,r_i]\)被覆盖,要保证\([1,r_j]\)被感染到\(l_i\)之前被及时治疗
最初我觉得有可能有第三个计划将\(l_i-1\)及时覆盖使得dp的条件放宽
但事实上这种方案可以归纳到第三个计划的更新
这样做是\(O(n^2)\)的
考虑优化,每一\(dp_i\)是由当前满足条件最小\(dp_j\)更新
类似与\(dijkstra\),可以用当前最小更新,模型为无向图,没有顺序,因为尽管线性\(dp\)按\(r_i\)排序,但\(r_i\)大的一定不会更新小的
将上式绝对值裁开,可以得到两个不等式
\[r_j-t_j+1\geq l_i-t_i(t_i<t_j) \\ r_j+t_j+1\geq l_i+t_i(t_i\geq t_j) \]用线段树维护最小,以\(t_i\)排序建树,树上查找后设为无穷,复杂度均摊\(O(nlog_2(n))\)
#include<bits/stdc++.h>
#define int long long
#define INF 1e14
#define ls 2*p
#define rs 2*p+1
using namespace std;
const int MAXM=1e5+5;
int n,m;
struct Task{
int t,l,r,c;
bool operator<(const Task x)const{
return t<x.t;
}
}a[MAXM];
int dp[MAXM];
struct node{
int val,u;
bool operator<(const node x)const{
return val>x.val;
}
};
priority_queue<node>q;
struct Seg{
int l,r;
int date1;
int date2;
}Tree[MAXM*4];
void push_up(int p)
{
Tree[p].date1=min(Tree[ls].date1,Tree[rs].date1);
Tree[p].date2=min(Tree[ls].date2,Tree[rs].date2);
}
void Build(int p,int l,int r)
{
Tree[p].l=l;
Tree[p].r=r;
if(l==r)
{
Tree[p].date1=a[l].l+a[l].t;
Tree[p].date2=a[l].l-a[l].t;
return;
}
int mid=(l+r)>>1;
Build(ls,l,mid);
Build(rs,mid+1,r);
push_up(p);
}
void Update(int p,int k)
{
if(Tree[p].l==Tree[p].r)
{
Tree[p].date1=INF;
Tree[p].date2=INF;
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(k<=mid)
{
Update(ls,k);
}
else
{
Update(rs,k);
}
push_up(p);
}
vector<int>avail;
void FindL(int p,int k1,int k2)
{
if(Tree[p].l==Tree[p].r)
{
// printf("%d %d&**\n",k2,Tree[p].date2);
if(Tree[p].date1<=k2)
{
avail.push_back(Tree[p].l);
}
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(mid>=k1)
{
if(Tree[ls].date1<=k2)
{
FindL(ls,k1,k2);
}
if(Tree[rs].date1<=k2)
{
FindL(rs,k1,k2);
}
}
else
{
if(Tree[rs].date1<=k2)
{
FindL(rs,k1,k2);
}
}
}
void FindR(int p,int k1,int k2)
{
if(Tree[p].l==Tree[p].r)
{
//printf("%d %d&**\n",k2,Tree[p].date2);
if(Tree[p].date2<=k2)
{
avail.push_back(Tree[p].l);
}
return;
}
int mid=(Tree[p].l+Tree[p].r)>>1;
if(mid<k1)
{
if(Tree[ls].date2<=k2)
{
FindR(ls,k1,k2);
}
if(Tree[rs].date2<=k2)
{
FindR(rs,k1,k2);
}
}
else
{
if(Tree[ls].date2<=k2)
{
FindR(ls,k1,k2);
}
}
}
signed main()
{
// freopen("01-01.in","r",stdin);
memset(dp,0x3f,sizeof(dp));
scanf("%lld %lld",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld %lld %lld %lld",&a[i].t,&a[i].l,&a[i].r,&a[i].c);
}
sort(a+1,a+1+m);
Build(1,1,m);
for(int i=1;i<=m;i++)
{
if(a[i].l==1)
{
// printf("%d %d-\n",i,a[i].c);
node sf;
sf.u=i;
sf.val=a[i].c;
dp[i]=a[i].c;
q.push(sf);
Update(1,i);
}
}
int tot=0;
while(q.size())
{
node temp=q.top();
// printf("%lld %lld--\n",temp.u,temp.val);
q.pop();
if(a[temp.u].r==n)
{
printf("%lld",temp.val);
return 0;
}
avail.clear();
FindL(1,temp.u,a[temp.u].r+a[temp.u].t+1);
for(int i=0;i<avail.size();i++)
{
int vv=avail[i];
dp[vv]=dp[temp.u]+a[vv].c;
Update(1,vv);
node fgf;
fgf.u=vv;
fgf.val=dp[vv];
q.push(fgf);
// printf("%d %d\n",vv,dp[vv]);
}
// printf("\n");
avail.clear();
FindR(1,temp.u,a[temp.u].r-a[temp.u].t+1);
for(int i=0;i<avail.size();i++)
{
int vv=avail[i];
dp[vv]=dp[temp.u]+a[vv].c;
Update(1,vv);
node fgf;
fgf.u=vv;
fgf.val=dp[vv];
q.push(fgf);
// printf("%d %d\n",vv,dp[vv]);
}
// ++tot;
// printf("%d\n",tot);
}
printf("-1");
}
标签:date1,date2,int,Tree,mid,DP,简单,dp 来源: https://www.cnblogs.com/kid-magic/p/16428842.html