洛谷P4742 [Wind Festival]Running In The Sky DAG dp + tarjan缩点

2 篇文章 0 订阅

题目背景

[Night - 20:02[Night−20:02 P.M.]P.M.]

夜空真美啊……但是……快要结束了呢……

题目描述

一天的活动过后,所有学生都停下来欣赏夜空下点亮的风筝。CurtisCurtis NishikinoNishikino想要以更近的视角感受一下,所以她跑到空中的风筝上去了(这对于一个妹子来说有点匪夷所思)! 每只风筝上的灯光都有一个亮度 k_iki​. 由于风的作用,一些风筝缠在了一起。但是这并不会破坏美妙的气氛,缠在一起的风筝会将灯光汇聚起来,形成更亮的光源!

CurtisCurtis NishikinoNishikino已经知道了一些风筝间的关系,比如给出一对风筝(a,b)(a,b), 这意味着她可以从 aa 跑到 bb 上去,但是不能返回。

现在,请帮她找到一条路径(她可以到达一只风筝多次,但只在第一次到达时她会去感受上面的灯光), 使得她可以感受到最多的光亮。同时请告诉她这条路径上单只风筝的最大亮度,如果有多条符合条件的路径,输出能产生最大单只风筝亮度的答案。

输入格式

第一行两个整数 nn 和 mm. nn 是风筝的数量, mm 是风筝间关系对的数量.

接下来一行 nn 个整数 k_iki​.

接下来 mm 行, 每行两个整数 aa 和 bb, 即CurtisCurtis可以从 aa 跑到 bb.

输出格式

一行两个整数。CurtisCurtis在计算出的路径上感受到的亮度和,这条路径上的单只风筝最大亮度.

输入输出样例

输入 #1复制

5 5
8 9 11 6 7
1 2
2 3
2 4
4 5
5 2

输出 #1复制

41 11

说明/提示

对于 20\%20% 的数据, 0<n \le 5\times10^3, \ 0 < m \le 10^40<n≤5×103, 0<m≤104.

对于 80\%80% 的数据, 0 < n \le 10^5, \ 0 < m \le 3\times10^50<n≤105, 0<m≤3×105.

对于 100\%100% 的数据, 0<n\le2\times10^5,\ 0<m\le5\times10^5,\ 0<k\le2000<n≤2×105, 0<m≤5×105, 0<k≤200.

一道DAGdp 的算是简单题吧

我错误的思想一直卡55分

wa了n发

最后看了题解写了topu序dp过了 枚举indu跑会浪费很多时间

先说思路:

拿到题 发现可以重复走 那就是有环了 然后要求最大链长 很容易想到 树形dp 求树的直径

两者结合 就大概是一道 DAG dp

去掉环 用tarjan缩点 博客有板子

记录每个颜色块的最大值

跑一遍topu序dp

结束

放一下代码吧

一开始不停的超时觉得是优化没到位 一直加黑科技优化QAQ

#pragma GCC optimize(2) 
#pragma GCC optimize(3) 
#pragma GCC optimize("Ofast") 
#pragma GCC optimize("inline") 
#pragma GCC optimize("-fgcse") 
#pragma GCC optimize("-fgcse-lm") 
#pragma GCC optimize("-fipa-sra") 
#pragma GCC optimize("-ftree-pre") 
#pragma GCC optimize("-ftree-vrp") 
#pragma GCC optimize("-fpeephole2") 
#pragma GCC optimize("-ffast-math") 
#pragma GCC optimize("-fsched-spec") 
#pragma GCC optimize("unroll-loops") 
#pragma GCC optimize("-falign-jumps") 
#pragma GCC optimize("-falign-loops") 
#pragma GCC optimize("-falign-labels") 
#pragma GCC optimize("-fdevirtualize") 
#pragma GCC optimize("-fcaller-saves") 
#pragma GCC optimize("-fcrossjumping") 
#pragma GCC optimize("-fthread-jumps") 
#pragma GCC optimize("-funroll-loops") 
#pragma GCC optimize("-fwhole-program") 
#pragma GCC optimize("-freorder-blocks") 
#pragma GCC optimize("-fschedule-insns") 
#pragma GCC optimize("inline-functions") 
#pragma GCC optimize("-ftree-tail-merge") 
#pragma GCC optimize("-fschedule-insns2") 
#pragma GCC optimize("-fstrict-aliasing") 
#pragma GCC optimize("-fstrict-overflow") 
#pragma GCC optimize("-falign-functions") 
#pragma GCC optimize("-fcse-skip-blocks") 
#pragma GCC optimize("-fcse-follow-jumps") 
#pragma GCC optimize("-fsched-interblock") 
#pragma GCC optimize("-fpartial-inlining") 
#pragma GCC optimize("no-stack-protector") 
#pragma GCC optimize("-freorder-functions") 
#pragma GCC optimize("-findirect-inlining") 
#pragma GCC optimize("-fhoist-adjacent-loads") 
#pragma GCC optimize("-frerun-cse-after-loop") 
#pragma GCC optimize("inline-small-functions") 
#pragma GCC optimize("-finline-small-functions") 
#pragma GCC optimize("-ftree-switch-conversion") 
#pragma GCC optimize("-foptimize-sibling-calls") 
#pragma GCC optimize("-fexpensive-optimizations") 
#pragma GCC optimize("-funsafe-loop-optimizations") 
#pragma GCC optimize("inline-functions-called-once") 
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 10;
int head[maxn];
int dfn[maxn];
int low[maxn];
bool vis[maxn];
int colour[maxn];
int indu[maxn];
int dist[maxn];
int light[maxn];
int cp[maxn];
int dp[maxn][5];
int mx[maxn];
int tot;
int tot1;
int sum;//联通块个数&&颜色
int dex;
int cnt;
int n, m;
int ans;
struct node{
	int to;
	int next;
	int from;
	node() {}
	node(int a, int b, int c) : to(a), next(b), from(c) {}
}edge[maxn], edge1[maxn];

void edgeadd(int a, int b){
	edge[tot] = node(b, head[a], a);
	head[a] = tot++;
}

void edgeadd1(int a, int b){
	edge1[tot1] = node(b, head[a], a);
	head[a] = tot1++;
}


void init(){
	memset(head, -1, sizeof(head));
	memset(vis, 0, sizeof(vis));
	memset(dfn, 0, sizeof(dfn));
	memset(indu, 0, sizeof(indu));
	memset(cp, 0, sizeof(cp));
	tot = 0;
	tot1 = 0;
	sum = 0;
    dex = 0;
	ans = 0;
}
stack<int> s;

void tarjan(int u){
	dfn[u] = low[u] = ++dex;
	s.push(u);
	vis[u] = 1;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if(vis[v]){
			low[u] = min(low[u], dfn[v]);
		}
	}
	if(low[u] == dfn[u]){
		sum++;
		while(1){
			int now = s.top();
			//cout << now << endl;
			colour[now] = sum;
			s.pop();
			vis[now] = 0;
			if(now == u) break;
		}
	}
}

void tsort(){
    queue<int>q;
    for (int i = 1; i <= n; i++) dp[i][0] = cp[i], dp[i][1] = mx[i];
    //dp[i][0] 维护的是走到 i 的点权和,同时 dp[i][1] 维护路径上的点权最大值。
    for (int i = 1; i <= sum; i++) if (!indu[i]) q.push(i);
    while(!q.empty()){
        int nw = q.front(); 
		q.pop();
        for (int i = head[nw]; i != -1; i = edge1[i].next){
            int v = edge1[i].to;
            if (dp[nw][0] + cp[v] > dp[v][0]){
                dp[v][0] = dp[nw][0] + cp[v];
                dp[v][1] = max(dp[nw][1], mx[v]);//注意这句和下面更新方法的差异
            }
            if (dp[nw][0] + cp[v] == dp[v][0])
                dp[v][1] = max(dp[v][1], dp[nw][1]);
            indu[v]--;
			if(indu[v] <= 0) q.push(v);
        }
    }
}
/*void dfs(int u, int pre){
    if(dp[pre][0]+cp[u]>dp[u][0]){
        dp[u][0]=dp[pre][0]+cp[u];
        dp[u][1]=max(dp[pre][1],mx[u]);//注意这句和下面更新方法的差异
        }
    if(dp[pre][0]+cp[u]==dp[u][0])
        dp[u][1]=max(dp[u][1],dp[pre][1]);
    for(int i = head[u]; i != -1; i = edge1[i].next){
        int v = edge1[i].to;
        if(v == pre)
            continue;
        dfs(v, u);
    }
}*/


int main(){
	scanf("%d%d", &n, &m);
	init();
	int x, y;
    for(int i = 1; i <= n; i++)
        scanf("%d", &light[i]);
	for(int i = 1; i <= m; i++){
		scanf("%d%d", &x, &y);
		edgeadd(x, y);
	}
	for(int i = 1; i <= n; i++){
		if(!dfn[i])
			tarjan(i);
	}
	memset(head, -1, sizeof(head));
	for(int i = 0; i < m; i++){
		int o, g;
		o = colour[edge[i].from];
		g = colour[edge[i].to];
		if(o != g){
			edgeadd1(o, g);
			indu[g]++;
		}
	}
    for(int i = 1; i <= n; i++){
		cp[colour[i]] += light[i];
		mx[colour[i]] = max(light[i], mx[colour[i]]);
    }
	/*for(int i = 1; i <= sum; i++){
		if(!indu[i]){
			dfs(i, 0);
		}
	}*/
	tsort();
	int res=1;
    for (int i = 2; i <= sum; i++)
        if (dp[i][0] > dp[res][0] || dp[i][0] == dp[res][0] && dp[i][1] >= dp[res][1]) res = i;
    printf("%d %d", dp[res][0], dp[res][1]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值