\(\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;
}
posted on 2022-04-11 08:13  Oxide  阅读(88)  评论(0编辑  收藏  举报