ZROI#961

Posted equinox-flower

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZROI#961相关的知识,希望对你有一定的参考价值。

ZROI#961

很诡异地一道题,你看他问的是是否存在距离\(d\in [dist,1.1dist]\)的路径.
你想一下这个\(1.1\)是个啥.好像不知道,先考虑暴力叭.
暴力你就\(bfs\),让点重复入队就好了,每个点维护一个\(set\),查询直接\(lower_bound\)即可.
\(Code:\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <ctime>
#include <map>
#include <set>
#define MEM(x,y) memset ( x , y , sizeof ( x ) )
#define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i)
#define per(i,a,b) for (int i = (a) ; i >= (b) ; -- i)
#define pii pair < int , int >
#define X first
#define Y second
#define rint read<int>
#define int long long
#define pb push_back

using std::queue ;
using std::set ;
using std::pair ;
using std::max ;
using std::min ;
using std::priority_queue ;
using std::vector ;
using std::swap ;
using std::sort ;
using std::unique ;
using std::greater ;

template < class T >
    inline T read () 
        T x = 0 , f = 1 ; char ch = getchar () ;
        while ( ch < '0' || ch > '9' ) 
            if ( ch == '-' ) f = - 1 ;
            ch = getchar () ;
        
       while ( ch >= '0' && ch <= '9' ) 
            x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
            ch = getchar () ;
       
   return f * x ;


const int N = 2e5 + 100 ;

struct edge  int to , next ; double data ;  e[N<<1] ;

set < double > p[N] ;
int n , m , Q , tot , head[N] ;
double dis[N] ;
bool vis[N] ;

inline void build (int u , int v , double w) 
    e[++tot].next = head[u] ; e[tot].to = v ;
    e[tot].data = w ; head[u] = tot ; return;


queue < int > q ;
inline void bfs (int cur) 
    vis[cur] = true ; q.push ( cur ) ;
    dis[cur] = 0 ; p[cur].insert ( dis[cur] ) ;
    while ( ! q.empty () ) 
        int j = q.front () ; q.pop () ; vis[j] = false ;
        for (int i = head[j] ; i ; i = e[i].next) 
            int k = e[i].to ;
            for (auto it : p[j]) 
                double dist = it + e[i].data ;
                if ( p[k].find ( dist ) == p[k].end () ) 
                    if ( ! vis[k] )  q.push ( k ) ; vis[k] = true ; 
                    p[k].insert ( dist ) ;
                
            
        
    
    return ;


signed main (int argc , char * argv[]) 
    n = rint () ; m = rint () ; Q = rint () ;
    rep ( i , 1 , m ) 
        int u = rint () , v = rint () , w = rint () ;
        build ( u , v , w ) ;
    
    bfs ( 1 ) ;
    while ( Q -- ) 
        int k = rint () ; double dist ;
        scanf ("%lf" , & dist ) ;
        auto pos = p[k].lower_bound ( dist ) ;
        if ( pos == p[k].end () ) puts ("NO") ;
        else if ( *pos <= dist * 1.1 ) puts ("YES") ;
        else puts ("NO") ;
    
    return 0 ;

然后我们发现,这个玩意儿的瓶颈在于转移的时候状态太多了.
然后我们考虑优化这个东西.
我们发现,如果说有三个距离\(x,y,z\)满足以下条件:
\[\cfracz1.1<x<y<z\]
那么\(y\)就是没用的,显然答案的存在性不会因为缺少\(y\)而改变.
于是我们每次合并两个距离集合(即上述代码的转移,本质是两个距离集合的合并)就可以把这些\(y\)踢掉,使用类似于归并排序的思路即可解决.
这样你可能会认为还是会\(TLE\),但实际上它的复杂度是对的,为什么呢?
因为\(1.1^450\)远远大于了所有权值的总和\(10^18\),也就是说一个点最多同时有\(450\)个状态被保留.
每次合并就是\(\Theta(450)\)的,查询随意,怎么搞都能过.
\(Code:\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <ctime>
#include <map>
#include <set>
#define MEM(x,y) memset ( x , y , sizeof ( x ) )
#define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i)
#define per(i,a,b) for (int i = (a) ; i >= (b) ; -- i)
#define pii pair < int , int >
#define one first
#define two second
#define rint read<int>
#define int long long
#define pb push_back
#define db double

using std::queue ;
using std::set ;
using std::pair ;
using std::max ;
using std::min ;
using std::priority_queue ;
using std::vector ;
using std::swap ;
using std::sort ;
using std::unique ;
using std::greater ;
using std::lower_bound ;

template < class T >
    inline T read () 
        T x = 0 , f = 1 ; char ch = getchar () ;
        while ( ch < '0' || ch > '9' ) 
            if ( ch == '-' ) f = - 1 ;
            ch = getchar () ;
        
       while ( ch >= '0' && ch <= '9' ) 
            x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
            ch = getchar () ;
       
   return f * x ;


const int N = 2e5 + 100 ;
const int M = 477 ;
struct edge  int to , next ; double data ;  e[N] ;
queue < int > q ; double tmp[N] , dis[N][M] ;
int n , m , Q , tot , head[N] , cnt[N] , deg[N] ;

inline void build (int u , int v , double w) 
    e[++tot].next = head[u] ; e[tot].to = v ;
    e[tot].data = w ; head[u] = tot ; return;


inline void merge (int x , int y , double val)  // 从 x 到 y
    int l = 1 , r = 1 , now = 0 ;
    while ( l <= cnt[x] && r <= cnt[y] ) 
        double cur = dis[x][l] + val ;
        if ( cur < dis[y][r] ) 
            while ( now >= 2 && (db)cur <= (db)tmp[now-1] * 1.1 && cur >= tmp[now] ) -- now ;
            tmp[++now] = cur ; ++ l ;
         else 
            while ( now >= 2 && (db)dis[y][r] <= (db)tmp[now-1] * 1.1 && dis[y][r] >= tmp[now] ) -- now ;
            tmp[++now] = dis[y][r] ; ++ r ;
        
    
    while ( l <= cnt[x] ) 
        double cur = dis[x][l] + val ;
        while ( now >= 2 && (db)cur <= (db)tmp[now-1] * 1.1 && cur >= tmp[now] ) -- now ;
        tmp[++now] = cur ; ++ l ;
    
    while ( r <= cnt[y] ) 
        while ( now >= 2 && (db)dis[y][r] <= (db)tmp[now-1] * 1.1 && dis[y][r] >= tmp[now] ) -- now ;
        tmp[++now] = dis[y][r] ; ++ r ;
    
    rep ( i , 1 , now ) dis[y][i] = tmp[i] ;
    cnt[y] = now ; return ;


inline void SPFA (int cur) 
    q.push ( cur ) ; ++ cnt[cur] ; dis[cur][cnt[cur]] = 0.0 ;
    while ( ! q.empty () ) 
        int j = q.front () ; q.pop () ;
        for (int i = head[j] ; i ; i = e[i].next) 
            int k = e[i].to ; -- deg[k] ;
            merge ( j , k , e[i].data ) ;
            if ( ! deg[k] ) q.push ( k ) ;
        
    
    return ;


signed main (int argc , char * argv[]) 
    n = rint () ; m = rint () ; Q = rint () ;
    rep ( i , 1 , m ) 
        int u = rint () , v = rint () ; db w ; scanf ("%lf" , & w ) ;
        build ( u , v , w ) ; ++ deg[v] ;
    
    SPFA ( 1 ) ;
    while ( Q -- ) 
        int k = rint () , dist = rint () ;
        int pos = lower_bound ( dis[k] + 1 , dis[k] + cnt[k] + 1 , dist ) - dis[k] ;
        if ( (db)dis[k][pos] > (db)1.1 * dist || pos == cnt[k] + 1 ) puts ("NO") ; else puts ("YES") ;
    
    return 0 ;

以上是关于ZROI#961的主要内容,如果未能解决你的问题,请参考以下文章

ZROI#1004

ZROI1119 十一·联考幸福

ZROI#1006

ZROI 537贪心题 题解

[ZROI]分组

ZROI week6