原题链接:https://www.luogu.com.cn/problem/CF960F
题意解读:按输入顺序从边中选出能首尾相连且权值递增的最多边数。
解题思路:
依然从最长上升子序列的模型出发,
对于输入的第i条边,用a[i],b[i],w[i]分别表示起点、终点、权值,
设f[i]表示第i条边结束的w的最长上升子序列长度,
则有f[i] = max(f[j]) +1,且需满足j < i, b[j] == a[i], w[j] < w[i]
似乎又是一个类似偏序的问题,第一维用来维护b,第二维用来维护w对应的最长上升子序列maxf
由于第一维每次只做相等判断,只需要用root[N]维护多个线段树的根节点,不需借助树状数组
第二维就是root[i]对应的权值线段树,节点值为w,维护信息为当前边的最长上升子序列maxf
只需要按输入顺序遍历所有边,
对于每条边a[i],b[i],w[i],在root[a[i]]中查询权值<w[i]的最大maxf,记为res,计算当前f[i] = res + 1
然后将root[b[i]]为根的线段树值为w[i]的maxf更新为f[i],不断递推即可求得所有f[i],记录最大值即可。
100分代码:
#include <bits/stdc++.h>
using namespace std;const int N = 100005;struct Node
{int L, R;int maxf;
} tr[N * 20];int root[N], idx;
int a[N], b[N], w[N], maxw;
int f[N]; //f[i]表示以第i条边结束的w的最长上升子序列长度
int n, m, ans;//在根为u的线段树中查询值<v的最大的maxf
int query(int u, int l, int r, int v)
{if(r < v) return tr[u].maxf;else if(l >= v) return 0;else{int mid = l + r >> 1;return max(query(tr[u].L, l, mid, v), query(tr[u].R, mid + 1, r, v));}
}//将根为u的线段树值v的maxf更新为f,如果f更大的话
int update(int u, int l, int r, int v, int f)
{if(!u) u = ++idx;tr[u].maxf = max(tr[u].maxf, f);if(l == r) return u;int mid = l + r >> 1;if(v <= mid) tr[u].L = update(tr[u].L, l, mid, v, f);else tr[u].R = update(tr[u].R, mid + 1, r, v, f);return u;
}int main()
{cin >> n >> m;for(int i = 1; i <= m; i++){cin >> a[i] >> b[i] >> w[i];maxw = max(maxw, w[i]);}for(int i = 1; i <= m; i++){f[i] = query(root[a[i]], 0, maxw, w[i]) + 1;ans = max(ans, f[i]);root[b[i]] = update(root[b[i]], 0, maxw, w[i], f[i]);}cout << ans;return 0;
}