题目链接:https://codeforces.ml/contest/960/problem/F
题目大意:
给出m条边,要求求出边权严格递增并且标号严格递增的一条最长的路径。
题目思路:
这个题目应该有多种做法
目前我只想到了主席树,后面的方法改天补上(已更新)。
方法一:
考虑dp状态转移:
对于题目给出的首先抽象一下,对于一条边(x,y,w),这条边只能由x结尾的边,边权小于w的转移过来。
所以就是要求每一个点x下,边权小于w的最大dp[i]
所以就可以建立n棵线段树,动态开一下点(其实本质思想已经成为主席树了)
对于每棵树求一下,[0,w-1]的最大值使其转移过来即可
/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=1e18;
const int maxn=1e6+6;
const int mod=998244353;
const double eps=1e-7;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
struct node{
int l,r;
int w;
}t[maxn*5];
ll dp[maxn];
int cnt = 0;
int root[maxn];
void add(int &x,int l,int r,int pos,int w){
if(!x) t[x=++cnt].w = w;
else t[x].w = max(t[x].w,w);
if(l==r) return ;
int mid =(l+r)/2;
if(pos<=mid) add(t[x].l,l,mid,pos,w);
else add(t[x].r,mid+1,r,pos,w);
}
ll query(int now,int l,int r,int x,int y){
if(x<=l&&y>=r) return t[now].w;
ll ans = -INF;
int mid = (l+r)/2;
if(x<=mid&&t[now].l)ans = max(ans,query(t[now].l,l,mid,x,y));
if(y>mid&&t[now].r) ans = max(ans,query(t[now].r,mid+1,r,x,y));
return ans;
}
int main(){
read(n);read(m);
ll ans = 0;
for(int i=1;i<=m;i++){
dp[i] = 1;
int x,y,z;scanf("%d%d%d",&x,&y,&z);
// debug(z-1);
ll temp = query(root[x],0,100000,0,z-1);
if(temp!=-INF) dp[i] = max(dp[i],temp + 1);
add(root[y],0,100000,z,dp[i]);
ans = max(ans,dp[i]);
}
printf("%lld\n",ans);
return 0;
}
/**
* 10 *
0000 1111
**/
方法二:
考虑到树状数组的空间利用,每次加入一个点对于对这个点有影响的节点有logn个。
所以说此时也可以动态开点。
就与游族杯的网络赛动态开点的字典树大同小异,我们用map存一下动态开点。
之后剩下的操作就是维护mp[u]下,小于pos的最大值
/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=1e18;
const int maxn=1e6+6;
const int mod=998244353;
const double eps=1e-15;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
map<int,int>mp[maxn];
void update(int u,int pos,int w){
if(!pos) mp[u][pos]=max(mp[u][pos],w);
else{
while(pos<=1000001){
mp[u][pos] = max(mp[u][pos],w);
pos+=pos&-pos;
}
}
}
int GetMax(int u,int pos){
int ans = mp[u][0];
while(pos>0){
ans = max(ans,mp[u][pos]);
pos -= pos&-pos;
}
return ans;
}
int dp[maxn];
int main(){
read(n);read(m);
int ans = 0;
for(int i=1;i<=m;i++){
dp[i] = 1;
int x,y,z;scanf("%d%d%d",&x,&y,&z);
dp[i] = max(dp[i],GetMax(x,z-1)+1);
update(y,z,dp[i]);
// debug(dp[i]);
ans = max(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}
/**
* 10 *
0000 1111
**/