CF459E Pashmak and Graph

CF459E Pashmak and Graph

题目描述

Pashmak’s homework is a problem about graphs. Although he always tries to do his homework completely, he can’t solve this problem. As you know, he’s really weak at graph theory; so try to help him in solving the problem.

You are given a weighted directed graph with nn vertices and mm edges. You need to find a path (perhaps, non-simple) with maximum number of edges, such that the weights of the edges increase along the path. In other words, each edge of the path must have strictly greater weight than the previous edge in the path.

Help Pashmak, print the number of edges in the required path.

输入格式

The first line contains two integers nn , mm (2<=n<=3·10^{5}; 1<=m<=min(n·(n-1),3·10^{5}))(2<=n<=3⋅105; 1<=m<=min(n⋅(n−1),3⋅105)) . Then, mm lines follows. The ii -th line contains three space separated integers: u_{i}ui​ , v_{i}vi​ , w_{i}wi​ (1<=u_{i},v_{i}<=n; 1<=w_{i}<=10^{5})(1<=ui​,vi​<=n; 1<=wi​<=105) which indicates that there’s a directed edge with weight w_{i}wi​ from vertex u_{i}ui​ to vertex v_{i}vi​ .

It’s guaranteed that the graph doesn’t contain self-loops and multiple edges.

输出格式

Print a single integer — the answer to the problem.

哇!英文题!

题意翻译

给定n 个点,m 条带权边的有向图。 现在请你找一条路径,起点和终点自取,在保证路径上的边权严格递增(即 下一条边的v 严格小于上一条的v)的情况下包含最多的边。 每条边只用一次。请输出路径最多能包含多少条边。

第一行输入2个数字n,m, 表示n 个点m 条有向边。 第2 到m+1 行每行3 个数s,t 和v,表示边的起点、终点、边权。

输入

3 3
1 2 1
2 3 2
3 1 3
输出

3

================== 手动分割线 ====================

我们可以将其转化为最长上升子序列

这时候我们可以按边权排序来更新每一个点

这样你就能拿到35分的好成绩

仅仅按边权排序是不行的

看下图

链接 6 和 5 的边如果先遍历到了,那么 6 的答案就会是2

而如果先遍历 3 和 5 这条边, 6 的答案就是 4

所以

在相同的边中,一定是起点答案大的优先遍历

因为小答案更新完可能需要被大答案重新更新

而先更新大答案就能保证更新的点一定不会被第二次更新

这样就行了吗?

这样你就还是能拿到35分的好成绩

还需要调整一个东西

在上图中,如果先遍历了 1 和 2 这条边, 3 就不能被 f [ 2 ] + 1 更新

也就是说,对于一些点

虽然不能用 f [ y ] = f [ x ] + 1 来更新,

但是可以用更新前的 x 点来更新

也就是 f [ y ] = f [ x ]

这样就可以 A 掉这道题了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
 
using namespace std;
 
const int N = 3e5 + 4;
 
struct stu{
    int x,y,z;
}e[N];
 
struct fuck{
    int x;
    int w;
    bool operator <(const fuck &x)const {
        return w > x.w;
    }
};
//让起点中答案的放在前面,就是关于答案的大根堆
 
int n,m;
int f[N];
int cnt[N];
 
priority_queue <fuck> que;
 
bool cmp(stu x,stu y)
{
    return x.z < y.z;
}
 
int read()
{
    int x=0,f=1;
    char ch;
    while(ch<'0'||ch>'9')  {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
//加点优化吧
 
void bfs()
{
    while(que.size())
    {
        int u = que.top().x;
        que.pop();
        int x = e[u].x;
        int y = e[u].y;
        int w = e[u].z;
        if(f[x] + 1 > f[y])
        {
            if(!cnt[x] || cnt[x] < w)
            {
                f[y] = f[x] + 1;
                cnt[y] = w;
            }
            else if(f[x] > f[y])
            {
                f[y] = f[x];
                cnt[y] = w;
            }
                // cnt 记录了更新这个点的上一个边权
                    //当前边比 cnt 大才能转移
                        //防止相同边来搞事情
        }
    }
}
 
int main()
{
    n = read();
    m = read();
    for(int i = 1;i <= m;i ++) e[i].x = read(),e[i].y = read(),e[i].z = read();
 
    sort(e + 1,e + 1 + m,cmp);
 
    e[0].z = -1;
    e[m + 1].z = -1;
    for(int i = 1;i <= m + 1;i ++)
    {
        if(e[i].z != e[i - 1].z) bfs();
            //相同边到此结束,遍历一下更新答案
 
        que.push((fuck){i,f[e[i].x]});
            //相同的边开始了
    }
    int ans = 0;
    for(int i = 1;i <= n;i ++)
        ans = max(ans,f[i]);
    cout << ans;
    return 0;
}
原文地址:https://www.cnblogs.com/xy0313/p/14073467.html