CF2024D(最短路)
题意
简单来说就是可以每次选择跳过当前题目或是做当前题目,做的话就得分并且来到上一个点,不做的话就损失这道题的分数,但可以跳到b[i]点上,也就是有可能会往后跳扩大做题范围
我们假设正确答案到达的最后面的点为t,那么最优解一定是从1一直跳到t,然后再把之前所有没写的都写一遍。那么最终答案就是a1到at的和减去1到t中所有跳过的ai和。不难想到,对于任意t,我们需要让从1跳到t的总代价最小就可以使t作为结尾时的答案最大,因为起点都是1,可以想到单源最短路。
如果我们做了一道题,那么我们可以直接跳到上一道题,代价为0,但如果我们跳过这道题,我们就相当于花了a[i]的代价连接到了b[i]。这样建边然后跑最短路,对于最后1到n循环求出每个点作为最靠后的t时的答案最大值。
话说虽然用的是markdown编辑,但我一直好像任何数学公式啥的都没用过主要是懒
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
const int maxn=1e6+10;
int dist[maxn];//1到i的最短距离
int vis[maxn];//是否用该点更新过答案
struct edge
{
int to;
int next;
int w;
}e[maxn];
int first[maxn],tot=0;
int a[maxn];
void add(int x,int y,int z)
{
tot++;
e[tot].w=z;
e[tot].to=y;
e[tot].next=first[x];
first[x]=tot;
}
struct node
{
int dis;
int pos;
bool operator <(const node &a)const
{
return a.dis<dis;
}
};
priority_queue<node> q;
void dij()
{
dist[1]=0;
q.push({0,1});
while(!q.empty())
{
node now=q.top();
q.pop();
int d=now.dis,p=now.pos;
if(vis[p]) continue;
vis[p]=1;
for(int i=first[p];i;i=e[i].next)
{
int y=e[i].to;
//cout<<p<<' '<<y<<endl;
if(dist[y]>dist[p]+e[i].w)
{
dist[y]=dist[p]+e[i].w;
if(!vis[y]) q.push({dist[y],y});
}
}
}
}
int n;
void youlinaixu()
{
cin>>n;
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++)
{
dist[i]=1e18;
vis[i]=0;
first[i]=0;
}
for(int i=1;i<=tot;i++)
{
e[i].to=e[i].next=e[i].w=0;
}
tot=0;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
a[i]=x;
if(i!=1) add(i,i-1,0);
}
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
add(i,x,a[i]);
}
dij();
int sum=0,ans=0;
for(int i=1;i<=n;i++)
{
//cout<<dist[i]<<endl;
sum+=a[i];
ans=max(ans,sum-dist[i]);
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--)
{
youlinaixu();
}
return 0;
}

浙公网安备 33010602011771号