CF2024D(最短路)

题意image
简单来说就是可以每次选择跳过当前题目或是做当前题目,做的话就得分并且来到上一个点,不做的话就损失这道题的分数,但可以跳到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;
}

posted @ 2025-06-11 20:44  miku今天吃什么  阅读(10)  评论(0)    收藏  举报