• 2020 EC Final D. City Brain


    传送门


    前几天我们队把ec final作为训练赛,没想到打的还挺不错,如果最后把这题做出来了,说不定还能拿au呢。


    不过这一场中间的真空期有点长,导致想出D的正解的时间有点晚了。

    首先对于两条路径,一定是中间有重合的一段(或者没有),而不是有两段,否则对于两个人中间分开的那一段路径一定可以一起选择一条更短的路走,从而将两段重合路径合并成了一段。

    想到这一点主算法就出来了,结合数据范围,可以\(O(n^2)\)枚举重合的路径的两个端点,那么重合的路径长度以及不重合的路径的长度就可以确定了,不妨记为\(a\)\(b\),那么问题就转化成,如何分配\(k_1,k_2(k_1+k_2=K)\),使提高限速后的路径和最短。


    首先合乎常理的是,限速一定要均匀分配,以不重合路径\(b\)为例,可如果以提高\(k_2\)次限速,那么最优解就是

    \[\sum\limits_{i=1}^b \frac1{\lfloor \frac{k_2}{n} \rfloor +1} + \sum\limits_{i=1}^{k_2 \mod b} \frac1{\lfloor \frac{k_2}{b} \rfloor +2} \]

    这个可以\(O(1)\)计算,对于重合路径方法相同,最后乘以2即可。

    又观察到这是一个单峰函数,而两个单峰函数的和也是单峰函数,因此我们可以三分,在\(O(\log n)\)时间内求出在给定\(a,b\)的前提下的最优分配方案。


    最后是对时间复杂度的优化。上述算法时间复杂度是\(O(n^2 \log n)\),会超时。优化在于我们只关注路径的长度,和结点没有关系,而重合路径的长度最多只有\(n\)种,因此我们可以先预处理对于长度为\(i\)的重合路径,其长度最短的不重合路径的长度,再进行三分。这样时间复杂度就是\(O(n^2 + n \log n)\).

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    In ll read() {ll x; scanf("%lld", &x); return x;}
    In void write(ll x) {printf("%lld", x);}
    const int maxn = 5145;
    
    int n, m, K, s[2], t[2];
    vector<int> ed[maxn];
    int dis[maxn][maxn], path[maxn];
    void bfs(int *dis, int s)
    {
        fill_n(dis, n + 15, INF);
        queue<int> q;
        q.push(s); dis[s] = 0;
        while(q.size())
        {
            int u = q.front(); q.pop();
            for(auto v : ed[u])
            {
                if(dis[v] == INF)
                {
                    dis[v] = dis[u] + 1; q.push(v);
                }
            }
        }
    }
    db calc(int t1, int t2, int s1, int s2)
    {
        db ans = 0;
        if(t1)
        {
    	    int t11 = s1 % t1;
    	    if(t11) ans += (1.0 * t11) / (s1 / t1 + 2);
    	    t11 = t1 - t11;
    	    if(t11) ans += (1.0 * t11) / (s1 / t1 + 1);
        }
        if(!t2) return ans;
        int t22 = s2 % t2;
        if(t22) ans += (2.0 * t22) / (s2 / t2 + 2);
        t22 = t2 - t22;
        if(t22) ans += (2.0 * t22) / (s2 / t2 + 1);
        return ans;
    }
    db solve(int t1, int t2)
    {
        int L = 0, R = K;
        db ret = 1e20;
        int lm, rm;
        db lret, rret;
        while(L < R)
        {
        	lm = (L + R) >> 1, rm = lm + 1;
            lret = calc(t1, t2, lm, K - lm), rret = calc(t1, t2, rm, K - rm);
            if(lret < rret)
            {
                R = rm - 1;
                ret = min(ret, lret);
            }
            else
            {
                L = lm + 1;
                ret = min(ret, rret);
            }
        }
        return ret;
    }
    
    int main()
    {
    	n = read(), m = read(), K = read();
    	for(int i = 1; i <= m; ++i)
        {
        	int u = read(), v = read();
            ed[u].push_back(v), ed[v].push_back(u);
        }
        s[0] = read(), t[0] = read(), s[1] = read(), t[1] = read();
    	for(int i = 1; i <= n; ++i) bfs(dis[i], i);
        fill_n(path, n + 1, INF);
        for(int u = 1; u <= n; ++u)
    		for(int v = 1; v <= n; ++v)		//要考虑不连通的情况 
            {
                if(dis[s[0]][u] == INF || dis[s[1]][u] == INF || dis[v][t[0]] == INF || dis[v][t[1]] == INF || dis[u][v] == INF) continue;
                int tp = dis[u][v];
                path[tp] = min(path[tp], dis[s[0]][u] + dis[s[1]][u] + dis[v][t[0]] + dis[v][t[1]]);
    			if(dis[t[1]][u] == INF || dis[v][s[1]] == INF) continue;
                path[tp] = min(path[tp], dis[s[0]][u] + dis[t[1]][u] + dis[v][t[0]] + dis[v][s[1]]);          
            }
        db ans = calc(dis[s[0]][t[0]] + dis[s[1]][t[1]], 0, K, 0);
        for(int i = 0; i <= n; ++i)
        	if(path[i] != INF) ans = min(ans, solve(path[i], i));
        printf("%.10lf", ans);
    }
    
  • 相关阅读:
    Delphi 使用字符串时,一个注意地方
    Delphi 字符串 详解
    Delphi SEH研究
    Delphi 新语法之Helper
    Delphi 判断一个二进制数中有多少个1
    Delphi 数据的理解
    Delphi 对象构造浅析后续
    Delphi 关于错误E2154 Type '%s' needs finalization not allowed in variant record
    Delphi 新语法介绍之For In
    Delphi 关于错误E1038 Unit identifier '%s' does not match file name
  • 原文地址:https://www.cnblogs.com/mrclr/p/15587629.html
Copyright © 2020-2023  润新知