time limit per test:1 second
memory limit per test:256 megabytes
standard input
standard output
Description
You are given a directed graph with n nodes and m edges, with all edges having a certain weight.
There might be multiple edges and self loops, and the graph can also be disconnected.
You need to choose a path (possibly passing through same vertices multiple times) in the graph such that the weights of the edges are in strictly increasing order, and these edges come in the order of input. Among all such paths, you need to find the the path that has the maximum possible number of edges, and report this value.
Please note that the edges picked don’t have to be consecutive in the input.
Input
The first line contains two integers n and m (1 ≤ n ≤ 100000,1 ≤ m ≤ 100000) — the number of vertices and edges in the graph, respectively.
m lines follows.
The i-th of these lines contains three space separated integers ai, bi and wi (1 ≤ ai, bi ≤ n, 0 ≤ wi ≤ 100000), denoting an edge from vertex ai to vertex bi having weight wi
Output
Print one integer in a single line — the maximum number of edges in the path.
Examples
input1
3 3
3 1 3
1 2 1
2 3 2
output1
2
input2
5 5
1 3 2
3 2 3
3 4 5
5 4 0
4 5 8
output2
3
题目大意
给出带权,编号为输入顺序的有向边,求最长的编号上升权值上升的路径长度
Analysis
有两种考虑方法
①. 对于每个节点将走向它的边指向编号更大权值更大的边,线段树优化连边,跑一边拓扑。。。好复杂,注意新点个数与新边个数
#include<cstring>
#include<cstdio>
#include<algorithm>
#define M 100100
using namespace std;
int n,m,fir[M],nex[M],_fir[M],_nex[M],top,no[M*10],opl,opv,siz[M*10],opr,que[M*30],head,tail;
int st[M*30],u[M*120],nxt[M*120],tot,d[M*30],f[M*30],v[M*120],cnt,node[M],l1;
struct road{int x,y,e;}r[M];
bool cmp(road a,road b){return a.e<b.e;}
void link(int x,int y,int e){
v[++top]=e;u[top]=y;nxt[top]=st[x];st[x]=top;++d[y];
}
void clear(int w,int l,int r){
if(!siz[w])return;
int m=l+r>>1;no[w]=siz[w]=0;
clear(w+w,l,m);clear(w+w+1,m+1,r);
}
void inc(int w,int h,int t){
int tmp=++cnt;++siz[w];
if(no[w])link(tmp,no[w],0);
link(no[w]=tmp,opv,1);
if(h==t)return;
int m=h+t>>1;
if(opl<=m)inc(w+w,h,m);else inc(w+w+1,m+1,t);
}
void con(int w,int h,int t){
if(opl>opr)return;
if(h>=opl && t<=opr){if(no[w])link(opv,no[w],0);return;}
int m=h+t>>1;
if(opl<=m)con(w+w,h,m);if(opr>m)con(w+w+1,m+1,t);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d %d %d",&r[i].x,&r[i].y,&r[i].e);
for(int i=1;i<=m;i++){
nex[i]=fir[r[i].x];fir[r[i].x]=i;
_nex[i]=_fir[r[i].y];_fir[r[i].y]=i;
}cnt=m;
for(int i=1;i<=n;i++){
l1=0;
for(int o=fir[i];o;o=nex[o])node[++l1]=o;
for(int o=_fir[i],a=1;o;o=_nex[o]){
while(a<=l1 && o<node[a]){
opl=r[node[a]].e;opv=node[a++];inc(1,0,100000);
}opl=r[o].e+1;opr=100000;opv=o;con(1,0,100000);
}
clear(1,0,100000);
}
for(int i=1;i<=cnt;i++)if(!d[i])que[++tail]=i,f[i]=i<=m;
for(;head^tail;){
int x=que[++head];
for(int i=st[x];i;i=nxt[i]){
f[u[i]]=max(f[u[i]],f[x]+v[i]);d[u[i]]--;
if(!d[u[i]])que[++tail]=u[i];
}
}int ans=0;
for(int i=1;i<=m;i++)if(f[i]>ans)ans=f[i];
printf("%d",ans);
return 0;
}
②. DP,设 f(i,k) f ( i , k ) 表示到了第i个点上一条边权值为k的最长路径。那么按照输入顺序的边进行操作,对于每个i建一棵动态开点的权值线段树即可
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 2001001
#define W 100000
using namespace std;
int n,m,rt[N],son[N][2],mx[N],cnt,opl,opr,opv;
int query(int w,int h,int t){
if(!w)return 0;
if(opl>opr)return 0;
if(h>=opl && t<=opr)return mx[w];
int m=h+t>>1,r=0;
if(opl<=m)r=max(r,query(son[w][0],h,m));
if(opr>m)r=max(r,query(son[w][1],m+1,t));
return r;
}
void inc(int &w,int h,int t){
if(!w)w=++cnt;mx[w]=max(mx[w],opv);
if(h==t)return;
int m=h+t>>1;
if(opl<=m)inc(son[w][0],h,m);else inc(son[w][1],m+1,t);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)rt[i]=++cnt;
for(int i=1;i<=m;i++){
int x,y,v,w;scanf("%d %d %d",&x,&y,&w);
opl=0;opr=w-1;v=query(rt[x],0,W)+1;
opl=w;opv=v;inc(rt[y],0,W);
}int ans=0;
for(int i=1;i<=n;i++)ans=max(ans,mx[rt[i]]);
printf("%d\n",ans);
return 0;
}