【Codeforces 960F】Pathwalks 主席树 动态树状数组

题目链接: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
**/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章