【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
**/

 

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