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\)满足以下条件:
\[\cfrac{z}{1.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 ;
}

原文地址:https://www.cnblogs.com/Equinox-Flower/p/11536113.html

时间: 2024-08-30 17:52:51

ZROI#961的相关文章

ZROI 普及组模拟赛02总结

ZROI 普及组模拟赛02总结 先放[网址][http://zhengruioi.com/contest/96] 可能是有一段时间没有打这种正式的比赛了,今天打的很奇怪... T1 模拟水题 既然是普及组模拟赛T1还是比较良心的 20分钟就过掉了 T2 <论不仔细观察题目导致的惨案> 没有发现莫尔斯电码非常的全 所以应该枚举哪些不行,而不是枚举26个字母 但是根据计算\(2*26*1e6\)也能跑过去啊 不知道为什么就是超时了... 最可笑的是还卡了20min的常数,还自己造了几组1e6的数据

ZROI提高组模拟赛05总结

ZROI提高组模拟赛05总结 感觉是目前为止最简单的模拟赛了吧 但是依旧不尽人意... T1 有一半的人在30min前就A掉了 而我花了1h11min 就是一个简单的背包,我硬是转化了模型想了好久,生生把一个弱智题变成了一个不可做题 最后竟然在转化两次后的模型上以为自己做出来了 这个题比别人多花的1h左右的时间,而且只得到了30分,成为了这场比赛失败的关键因素 T2 依旧是一道简单题 有人20min之内就A掉了 感觉放在CF里最多算一道Div2 D,还是简单的那种 可是我又一次想复杂了 大意就是

【ZROI 537】贪心题 题解

[ZROI 537]贪心题 题解 Link Solution 最大的一边直接放到一起贪心即可 着重讲小的一边 已知对于二分图匹配,其答案即为最大流 令时间集合为 \(T = {1,2,3,\dots,maxt}\) 对于每一门课程,按照如下方式建图: 每个任务为一个点,每个时间为一个点,每个任务向其对应的时间区间连边,源点向每个任务连边,边权为 \(1\),每个时间向汇点连边,边权为 \(1\) 考虑第一门课程: 我们选择一些时间节点分给它,设为 \(T_1\) 假设最大流中任务集合为 \(A\

961. N-Repeated Element in Size 2N Array

961. N-Repeated Element in Size 2N Array 题目概述: 在大小为 2N 的数组 A 中有 N+1 个不同的元素,其中有一个元素重复了 N 次. 返回重复了 N 次的那个元素. 解法一 class Solution { public int repeatedNTimes(int[] A) { int index = 0; Set<Integer> set = new LinkedHashSet<>(); for (int i = 0; i <

ZROI WC Round5 题解

ZROI WC Round5 题解 Problem A 题意 给定一个长度为 \(n\) 的序列,操作是交换两个相邻的数,要求将序列变成先单调不降再单调不升,求最小操作数,注意可以完全单调不降或者完全单调不升 想法 发现最小的数一定在最左侧或者最右侧 有一个暴力的做法是按照从小到大的顺序,每次看向哪边比较近就交换到哪一侧,由于不管交换到哪一个剩下的序列都是一样的,所以这个做法是正确的 下面就是优化这个算法,不难发现一个数如果移动到最左侧,那么它左侧的比它小的数肯定都移动到左侧,所以可以用树状数组

ZROI week6

ZROI week6 T1 用一个类似背包的东西记录答案. T2 好像直接用|操作即可. T3 瞎搞就完事了 T4 启发式合并,然而变量写错了,就没了... 总结 100 + 100 + 100 + 0 = 300 原文地址:https://www.cnblogs.com/akoasm/p/10306998.html

ZROI#960

ZROI#960 先说\(\Theta(n^2)\)暴力叭. 显然的想法就是枚举答案中相邻两个\(1\)之间的\(0\)的个数. 然后贪心匹配,注意判断最后一段是否满足限制. #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <string> #include <vec

ZROI#1006

ZROI#1006 可能一眼看起来是个很不可做的题,但你仔细思考一下,你发现.....给的是个\(n\)个点\(n\)条边的东西... 那么它可能是个啥呢? 是个基环树?是个森林+环?是个基环树+森林? 都是有可能的! 然后我们发现,答案就是连通块个数+环数-1. 为什么呢? 假设有\(x\)个连通块,\(y\)个环,那么对于连通块我们一定有一个作为答案连通块,其余的都要接在这下面,且只需修改根即可,所以连通块对答案的贡献是\(x-1\),而对于环,必须拆开,且只需要断开一处成为链接在答案连通块

【CodeForces】961 F. k-substrings 字符串哈希+二分

[题目]F. k-substrings [题意]给定长度为n的串S,对于S的每个k-子串$s_ks_{k+1}...s_{n-k+1},k\in[1,\left \lceil \frac{n}{2} \right \rceil]$,找到满足[奇数长度][严格子串][同时是前缀和后缀]的最长子串.n<=10^6. [算法]字符串哈希+二分 [题解]任意两个对应子串,它们有一个不变量--它们的中心一定是i和n-i+1.而且固定中心之后,能延伸的最长相等子串是可以二分+哈希得到的. 所以枚举k,二分+