題目鏈接: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
**/