• [Contest on 2022.4.9] 梦回 csp 赛场


    \(\cal T_1\) 回路

    \(\mathbb{Description}\)

    给定一张 \(n\) 个点的无向图,定义经过一个点 \(u\) 的非平凡回路为一条从 \(u\) 出发回到 \(u\) 的路径,并且至少包含一个简单环。你需要对于每个点,求出经过它的最小非平凡回路的长度 \(L\). 这个问题很困难,因此你只需要求出 \(\lceil L\rceil\) 即可。特别的,如果不存在经过它的非平凡回路,输出 \(-1\).

    \(n\le 5000\).

    \(\mathbb{Solution}\)

    一些闲话:我真的是哔了狗了。

    这题真的完完全全想偏了:一来就往 \(\rm dfs\) 树上靠,大体想法是找出每个点所在的最小环,然后再跑一个 \(\rm bfs\) 解决问题。对于最小环,就通过返祖边修改此边覆盖的所有点的最小环(是的,我它吗还写了个树剖套 \(\rm zkw\) 的两 \(\log\) 伞兵算法)。对于多源点 \(1\) 边权最短路,应该是 \(\mathcal O(m)\) 的,因为每个点至多入队两次(初始作为源点入队,被更新入队,是的,所以我的队列用的是优先队列,所以还要带 \(\log\))。然后就写写写,最后发现 "环" 不止由一条返祖边与树边组成,我就傻了。

    实际上这题的思路非常简单:枚举每个点作为起点,直接开始 \(\rm bfs\),假设当前点为 \(u\),如果发现 \(v\) 之间被遍历过且 \(u\) 不是从 \(v\) 拓展而来,此时的 \(d_u\) 就是答案。复杂度 \(\mathcal O(n^2)\).

    \(\mathbb{Code}\)

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while(!isdigit(s=getchar())) f|=(s=='-');
    	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    	return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
    	static int writ[50], w_tp=0;
    	if(x<0) putchar('-'), x=-x;
    	do writ[++w_tp]=x-x/10*10,x/=10; while(x);
    	while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <queue>
    # include <vector>
    # include <cstring>
    using namespace std;
    
    const int maxn = 5005;
    
    queue <int> q;
    char s[maxn];
    vector <int> e[maxn];
    int n,dis[maxn];
    
    int main() {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        n=read(9);
        for(int i=2;i<=n;++i) {
            scanf("%s",s+1);
            for(int j=1;j<=i;++j) if(s[j]=='1')
                e[i].emplace_back(j), e[j].emplace_back(i);
        }
        int ans;
        for(int i=1;i<=n;++i) {
            memset(dis,0,sizeof(int)*(n+2));
            while(!q.empty()) q.pop();
            q.push(i), dis[i]=1, ans=-1;
            while(!q.empty()) {
                int u=q.front(); q.pop();
                for(const auto& v:e[u])
                    if(dis[v] && dis[v]+1==dis[u]);
                    else if(dis[v]) { ans=dis[u]; break; }
                    else dis[v]=dis[u]+1, q.push(v);
                if(~ans) break;
            }
            print(ans,'\n');
        }
        return 0;
    }
    

    \(\cal T_2\)

    \(\mathbb{Description}\)

    \(\mathbb{Solution}\)

    \(\mathbb{Code}\)

    
    

    \(\cal T_3\) 追逐

    \(\mathbb{Description}\)

    \(\rm Alice\)\(\rm Bob\) 在一棵 \(n\) 个点的树上追逐,树上每条边的长度都是 \(1\)\(\rm Alice\) 先选定自己的起始位置,然后 \(\rm Bob\) 选定自己的起始位置,随后 \(\rm Alice\) 逃跑,\(\rm Bob\)\(\rm Alice\)\(\rm Alice\) 每步可以走至多 \(d_a\) 的长度,\(\rm Bob\) 每步可以走至多 \(d_b\) 的长度。\(\rm Bob\) 追上 \(\rm Alice\) 当且仅当某次 \(\rm Bob\) 操作之后,\(\rm Bob\)\(\rm Alice\) 处于同一个节点上(选位置的那一个轮次不算)。

    但是由于一些不可控力因素,第 \(i\) 条边有 \(p_i\) 的概率会断开。你想要求出,\(\rm Bob\) 能够成功追上 \(\rm Alice\) 的概率是多少。由于大家都不喜欢小数,你只需要输出答案对 \(998244353\) 取模的结果即可。注意 \(\rm Alice\)\(\rm Bob\) 选点的时候是所有边已经按照自己的概率断开的时候。

    \(n\le 2\cdot 10^6,1\le d_a,d_b\le 10^9\).

    \(\mathbb{Solution}\)

    首先这题有一个先导结论:\(\rm Bob\) 能追上 \(\rm Alice\) 当且仅当 \(d_a\le 2d_b\) 或树上直径 \(\le 2d_b\).

    对于树上直径 \(\le 2d_b\) 的情况,\(\rm Bob\) 只需要选择某条直径的中点即可,此时无论 \(\rm Alice\) 跳到哪个点,\(\rm Bob\) 都能直接跳过去,因为树上每个点到他的距离均 \(\le d_b\);反之,就意味着存在直径 \(>2d_b\),如果还满足 \(d_a\le 2d_b\)\(\rm Bob\) 只需要最初选点时距离 \(\rm Alice\)\(d_b\)(这是一定可行的),然后一直维持 \(\ge d_b\) 的距离,总能将 \(\rm Alice\) 逼入绝境,因为 \(\rm Alice\)\(\rm Bob\) 相对而跳一定是个死。而如果 \(d_a>2d_b\)\(\rm Alice\) 就可以把 \(\rm Bob\) 钓来钓去(初始时选在直径上,之后就一直在直径上跳),当二者距离 \(\le d_b\) 时,对着他跳走即可。草怎么这么像渣女和病娇的故事好带感斯哈斯哈

    \(\text{Subtask 1}\)\(n\le 2000\)

    有了上面的结论,我们只需要算出整棵树的直径 \(\le 2d_b\) 的概率即可,令 \(dp_{u,i}\) 为节点 \(u\) 的子树最深深度为 \(i\) 且直径不超过 \(2d_b\) 的概率,转移有

    \[\begin{align} dp_{u,i}=&\ p\cdot dp'_{u,i}\cdot \sum_{j=0}^{2d_b}dp_{v,j}+\\ &(1-p)\cdot \left(\sum_{j=0}^{\min\{i-1,2d_b-i-1\}}dp_{v,j}\cdot dp'_{u,i}+\sum_{j=0}^{\min\{i-1,2d_b-i\}}dp_{v,i-1}\cdot dp'_{u,j} \right) \end{align} \]

    \(\min\{i-1,2d_b-i\}\) 而不是 \(\min\{i,2d_b-i\}\) 的原因是避免算重。前缀和优化一下就是 \(\mathcal O(n^2)\) 的了。

    \(\text{Subtask 2}\):树为一条链

    利用初始时只有 \(f_{u,0}=1\) 的性质,我们可以简化上面的 \(\mathtt{dp}\) 式:

    \[dp_{u,0}=p\cdot \sum_{j=0}^{2d_b}dp_{v,j},dp_{u,i}=(1-p)\cdot dp_{v,i-1} \]

    这实际上就是区间乘 + 单点插入,用线段树可以简单维护。

    \(\mathbb{Code}\)

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x : x;
    }
    template <class T>
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp]=x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <vector>
    # include <iostream>
    using namespace std;
    typedef pair <int,int> par;
    
    const int ONE = 1e7;
    const int maxn = 2005;
    const int mod = 998244353;
    
    inline int inc(int x,int y) { return x+y>=mod?x+y-mod:x+y; }
    inline int dec(int x,int y) { return x-y<0?x-y+mod:x-y; }
    inline int inv(int x,int y=mod-2) {
        int r=1;
        for(; y; y>>=1, x=1ll*x*x%mod)
            if(y&1) r=1ll*r*x%mod;
        return r;
    } const int Inv = inv(ONE);
    
    int n,d_a,d_b,dp[maxn][maxn],lim;
    vector <par> e[maxn];
    
    void dfs(int u,int fa) {
        int tmp[maxn]; dp[u][0]=1;
        for(int i=0;i<=lim;++i) tmp[i]=1;
        for(const auto& v:e[u]) if(v.first^fa) {
            dfs(v.first,u); int to=v.first, p=v.second, coe;
            for(int i=0;i<=lim;++i) {
                dp[u][i] = inc(
                    1ll*p*dp[u][i]%mod*dp[to][lim]%mod,
                    1ll*dec(1,p)*dp[to][min(i-1,lim-i-1)]%mod*dp[u][i]%mod
                );
                if(i) {
                    if(i==1) coe = dp[to][0];
                    else coe = dec(dp[to][i-1],dp[to][i-2]);
                    dp[u][i] = inc(
                        dp[u][i],
                        1ll*coe*tmp[min(i-1,lim-i)]%mod*dec(1,p)%mod
                    );
                }
            }
            tmp[0]=dp[u][0];
            for(int i=1;i<=lim;++i) tmp[i] = inc(tmp[i-1],dp[u][i]); 
        }
        for(int i=1;i<=lim;++i) dp[u][i] = inc(dp[u][i-1],dp[u][i]);
    }
    
    int main() {
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
        n=read(9), d_a=read(9), d_b=read(9); lim = (d_b<<1);
        for(int i=1;i<n;++i) {
            int u=read(9), v=read(9), w=1ll*read(9)*Inv%mod;
            e[u].emplace_back(make_pair(v,w));
            e[v].emplace_back(make_pair(u,w));
        }
        if(d_a<=lim) return puts("1"), (0-0);
        dfs(1,0); print(dp[1][lim],'\n');
        return 0;
    }
    
  • 相关阅读:
    用java实现的微信公众号爬虫
    装饰模式
    输入一个数,查询该数是否为素数
    Machine-learning-DecisionTree
    Circles of Waiting
    GRE阅读
    云数据库POLARDB产品解读之二:如何做到高性价比
    奉上一份云上数据安全保护指南
    从双十一看阿里云安全的“创世纪”——采访阿里云安全掌门人肖力有感
    阿里云移动研发平台EMAS,是如何连续5年安全护航双11的?
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15002870.html
Copyright © 2020-2023  润新知