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;
}


目录
相关文章
|
17天前
|
算法 测试技术 C#
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
|
17天前
|
算法 测试技术 C#
【键值皆有序map 线段树 数学 】100240. 最小化曼哈顿距离
【键值皆有序map 线段树 数学 】100240. 最小化曼哈顿距离
|
4月前
【每日一题Day277】LC2569更新数组后处理求和查询 | 线段树
【每日一题Day277】LC2569更新数组后处理求和查询 | 线段树
14 0
|
4月前
【每日一题Day234】LC1171从链表中删去总和值为零的连续节点 | 链表模拟 哈希表+前缀和
【每日一题Day234】LC1171从链表中删去总和值为零的连续节点 | 链表模拟 哈希表+前缀和
20 0
|
6月前
|
算法 C++
剑指offer(C++)-JZ40:最小的K个数(算法-排序)
剑指offer(C++)-JZ40:最小的K个数(算法-排序)
|
6月前
|
算法 C++
剑指offer(C++)-JZ4:二维数组中的查找(算法-搜索算法)
剑指offer(C++)-JZ4:二维数组中的查找(算法-搜索算法)
|
11月前
|
存储
(序列)(贪心)(LIS)(区间dp)最少拦截系统
(序列)(贪心)(LIS)(区间dp)最少拦截系统
52 0
(树状数组,线段树)(数组模拟哈希)(解题步骤)acwing数星星
(树状数组,线段树)(数组模拟哈希)(解题步骤)acwing数星星
67 0
|
机器学习/深度学习 人工智能 Java