CF960F. Pathwalks (树上二维LIS 线段树 动态开点树状数组)

简介: CF960F. Pathwalks (树上二维LIS 线段树 动态开点树状数组)

linkkkk

题意:

给定 n 个点 m条边的有向图,可能不连通,可能有重边,也可能会有自环。求最长的路径(可以经过重复节点),使得这条路径的编号和权值都严格单调递增,其中编号指输入的顺序。路径的长度是指经过边的数量。

思路1:

要求编号也单调递增的话不妨按照输入顺序加边,之后只考虑让权值严格递增即可。

设d p [ i ] [ w ]表示以i为结尾并且最后一条边权为w的最长路径长度。那么该状态可以从所有以i为端点并且权值< w

现在要做的就是如何快速求出以j为起点的权值在[ 0 , w − 1 ]之间的长度最大值。

对每个节点都开一棵权值线段树,维护d p [ i ] [ 1 − 1 e 5 ]内最大值。单点更新,区间查询。

用动态开点来优化空间

由于权值可能为0,在输入的时候统一加1

代码1:

// Problem: CF960F Pathwalks
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF960F
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int>PII;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
inline void write(ll x){if (x < 0) x = ~x + 1, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');}
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
ll ksm(ll a, ll b,ll mod){ll res = 1;while(b){if(b&1)res=res*a%mod;a=a*a%mod;b>>=1;}return res;}
#define read read()
#define debug(x) cout<<#x<<":"<<x<<endl;
const int maxn=2e5+7,inf=0x3f3f3f3f;
int n,m;
int dp[maxn],root[maxn],idx;
struct node{
  int l,r,w;
}tr[maxn*40];
void update(int &p,int l,int r,int pos,int val){
  if(!p) p=++idx;
  if(l==r){
    tr[p].w=val;return ;
  }
  int mid=(l+r)/2;
  if(pos<=mid) update(tr[p].l,l,mid,pos,val);
  else update(tr[p].r,mid+1,r,pos,val);
  tr[p].w=max(tr[tr[p].l].w,tr[tr[p].r].w);
}
int query(int p,int l,int r,int ql,int qr){
  if(p==0) return 0;
  if(ql<=l&&r<=qr) return tr[p].w;
  int mid=(l+r)/2,ans=0;
  if(ql<=mid) ans=max(ans,query(tr[p].l,l,mid,ql,qr));
  if(qr>mid) ans=max(ans,query(tr[p].r,mid+1,r,ql,qr));
  return ans;
}
int main(){
  n=read,m=read;
  int ans=0;
  rep(i,1,m){
    int u=read,v=read,w=read+1;
    dp[v]=query(root[u],1,100000,1,w-1)+1;
    ans=max(ans,dp[v]);
    update(root[v],1,100000,w,dp[v]);
  }
  write(ans);
  return 0;
}

思路2:

由于只有单点修改和区间查询,可以用树状数组来代替线段树

  • 用map代替数组,实施动态开点

代码2:

// Problem: CF960F Pathwalks
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF960F
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int>PII;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
inline void write(ll x){if (x < 0) x = ~x + 1, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');}
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
ll ksm(ll a, ll b,ll mod){ll res = 1;while(b){if(b&1)res=res*a%mod;a=a*a%mod;b>>=1;}return res;}
#define read read()
#define debug(x) cout<<#x<<":"<<x<<endl;
const int maxn=2e5+7,inf=0x3f3f3f3f;
int n,m;
map<int,int>mp[maxn];
int dp[maxn];
int lowbit(int x){
  return x&-x;
}
void update(int u,int pos,int w){
  if(!pos) mp[u][pos]=max(mp[u][pos],w);  
  else{
    while(pos<maxn){
      mp[u][pos]=max(mp[u][pos],w);
      pos+=lowbit(pos);
    }
  }
}
int query(int u,int pos){
  int ans=mp[u][0];
  while(pos>0){
    ans=max(ans,mp[u][pos]);
    pos-=lowbit(pos);
  }
  return ans;
}
int main(){
  n=read,m=read;
  int ans=0;
  rep(i,1,m){
    int tmp=0;
    int u=read,v=read,w=read;
    tmp=query(u,w-1)+1;
    update(v,w,tmp);
    ans=max(ans,tmp);
  }
  write(ans);
  return 0;
}


目录
相关文章
|
6月前
【每日一题Day287】LC24 两两交换链表中的节点 | 模拟 递归
【每日一题Day287】LC24 两两交换链表中的节点 | 模拟 递归
44 0
|
6月前
D. Book of Evil(树的直径+换根dp)
D. Book of Evil(树的直径+换根dp)
|
6月前
|
编译器 数据安全/隐私保护
PTA 线性表 7-1 约瑟夫环(Josephus)问题(by Yan) (100分) 按出列次序输出每个人的编号
PTA 线性表 7-1 约瑟夫环(Josephus)问题(by Yan) (100分) 按出列次序输出每个人的编号
|
6月前
|
算法 测试技术 C#
【线段树 区间位运算模板】3117划分数组得到最小的值之和
【线段树 区间位运算模板】3117划分数组得到最小的值之和
|
6月前
|
算法 测试技术 C#
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
|
6月前
【每日一题Day277】LC2569更新数组后处理求和查询 | 线段树
【每日一题Day277】LC2569更新数组后处理求和查询 | 线段树
30 0
|
6月前
【每日一题Day220】LC1439有序矩阵中的第 k 个最小数组和 | 堆
【每日一题Day220】LC1439有序矩阵中的第 k 个最小数组和 | 堆
72 0
|
11月前
|
算法 测试技术 C#
C++二分查找算法的应用:将数据流变为多个不相交区间
C++二分查找算法的应用:将数据流变为多个不相交区间
|
算法
Leecode 25. K 个一组翻转链表
Leecode 25. K 个一组翻转链表
58 0
|
算法 程序员
【牛客算法BM2】 链表内指定区间反转
你好,欢迎来到我的博客!作为一名程序员,我经常刷LeetCode题目来提升自己的编程能力。在我的博客里,我会分享一些我自己做过的题目和解题思路,希望能够帮助到大家。今天,我想和大家分享一道挑战性较高的题目,让我们一起来挑战一下吧!作者也是在学习的过程中刷到有意思的题目就会选择与大家分享,并且提供较优解,关于力扣 的文章全部放在博客,如果大家喜欢记得支持作者。🤓