LibreOJ #2542.「PKUWC2018」随机游走 min-max容斥+树上高斯消元

本文介绍了一种利用树形动态规划方法求解在特定树结构中从起点出发遍历指定点集的期望步数的问题。通过将问题转换为求解到达点集中任一点即停止的期望步数,并采用树形DP进行高效求解。

题意

有一棵n个点的树,现在确定一个起点s,每次会从当前点随机选择一条相邻的边走过去。有q次询问,每次询问会给出一个点集,问如果在把点集中的每个点都至少遍历一遍后停止,期望要走的步数是多少。
n18n≤18

分析

首先min-max容斥一下,那么问题就变成了对于每个点集,若在到达了点集中的任意一个点就停止,则期望步数是多少。
考虑dp,设fx,sfx,s表示从xx开始走,然后点集为s的期望步数。
比较暴力的做法就是高斯消元,但注意到这是一棵树,那么就可以通过树形dp来消元。
具体来说就是把fx,sfx,s表示成kffax,s+bk∗ffax,s+b的形式,然后从下往上递推,如果现在知道了所有儿子的k,bk,b,就很容易得到当前点的k,bk,b,而根节点的k=0k=0,所以可以通过一遍dfs直接得出答案。
最后预处理一下前缀和然后O(1)O(1)询问即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

typedef long long LL;

const int N=20;
const int MOD=998244353;

int n,q,s,cnt,last[N],k[N],b[N],bin[N],f[1000005],deg[N];
bool vis[N];
struct edge{int to,next;}e[N*2];

int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y&1) ans=(LL)ans*x%MOD;
        x=(LL)x*x%MOD;y>>=1;
    }
    return ans;
}

void addedge(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}

void dfs(int x,int fa)
{
    if (vis[x]) {k[x]=b[x]=0;return;}
    int ny=ksm(deg[x],MOD-2);
    k[x]=ny;b[x]=1;
    int c=1;
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa) continue;
        dfs(e[i].to,x);
        (c+=MOD-(LL)k[e[i].to]*ny%MOD)%=MOD;
        (b[x]+=(LL)b[e[i].to]*ny%MOD)%=MOD;
    }
    c=ksm(c,MOD-2);
    k[x]=(LL)k[x]*c%MOD;b[x]=(LL)b[x]*c%MOD;
}

int main()
{
    scanf("%d%d%d",&n,&q,&s);
    bin[0]=1;
    for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2;
    for (int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        deg[x]++;deg[y]++;
        addedge(x,y);
    }
    for (int i=1;i<bin[n];i++)
    {
        memset(vis,0,sizeof(vis));
        for (int j=1;j<=n;j++)
            if (i&bin[j-1]) vis[j]=1;
        dfs(s,0);
        f[i]=b[s];
    }
    for (int i=0;i<n;i++)
        for (int j=0;j<bin[n];j++)
            if (j&bin[i]) (f[j]+=MOD-f[j^bin[i]])%=MOD;
    while (q--)
    {
        int tot,t=0;scanf("%d",&tot);
        for (int i=1;i<=tot;i++)
        {
            int x;scanf("%d",&x);
            t|=bin[x-1];
        }
        if (tot&1) printf("%d\n",f[t]);
        else printf("%d\n",(MOD-f[t])%MOD);
    }
    return 0;
}
function [global_best, global_best_val, convergence_curve] = improved_pso(obj_func, dim, lb, ub, max_iter, pop_size, varargin) % 参数解析 default_pCoef = 1e6 * ones(1,5); default_minConstr = [-inf, -inf, -inf, -inf, -inf]; default_maxConstr = [inf, inf, inf, inf, inf]; p = inputParser; addParameter(p, &#39;pCoefYueShuTiaoJian&#39;, default_pCoef); addParameter(p, &#39;min_YueShuTiaoJian&#39;, default_minConstr); addParameter(p, &#39;max_YueShuTiaoJian&#39;, default_maxConstr); parse(p, varargin{:}); % 算法参数 w_max = 0.9; w_min = 0.4; rho = 1.5; delta = 0.3; VelMax = 0.2 * (ub - lb); % 历史记录初始化 BestPositions = zeros(max_iter, dim); convergence_curve = zeros(max_iter, 1); % 粒子群初始化 particles = struct(... &#39;position&#39;, [], &#39;velocity&#39;, [], &#39;cost&#39;, inf,... &#39;Constr&#39;, zeros(1,5),... &#39;best&#39;, struct(&#39;position&#39;, [], &#39;cost&#39;, inf, &#39;Constr&#39;, zeros(1,5))... ); pop = repmat(particles, pop_size, 1); global_best = struct(&#39;position&#39;, [], &#39;cost&#39;, inf, &#39;Constr&#39;, zeros(1,5)); for i = 1:pop_size pop(i).position = lb + (ub-lb).*rand(1,dim); pop(i).velocity = rand(1,dim).*(2*VelMax) - VelMax; [pop(i).cost, pop(i).Constr] = obj_func(pop(i).position); pop(i).best = struct(... &#39;position&#39;, pop(i).position,... &#39;cost&#39;, pop(i).cost,... &#39;Constr&#39;, pop(i).Constr); if pop(i).cost < global_best.cost global_best = pop(i).best; end end % 主循环 for iter = 1:max_iter % 动态参数计算 pbest_cell = arrayfun(@(p)p.best.position, pop, &#39;UniformOutput&#39;, false); pbest_pos = vertcat(pbest_cell{:}); normalized_pbest = (pbest_pos - lb) ./ (ub - lb); sigma_per_dim = std(normalized_pbest,0,1); avg_sigma = mean(sigma_per_dim); % 自适应参数调整 theta = sigma_per_dim * pi/2; w = w_min + (w_max - w_min) * sigma_per_dim/0.2887; c1 = rho * sin(theta) + delta; c2 = rho * cos(theta) + delta; % 更新粒子 for i = 1:pop_size % 速度更新 r1 = rand(1,dim); r2 = rand(1,dim); cognitive = c1 .* r1 .* (pop(i).best.position - pop(i).position); social = c2 .* r2 .* (global_best.position - pop(i).position); pop(i).velocity = w .* pop(i).velocity + cognitive + social; pop(i).velocity = min(max(pop(i).velocity, -VelMax), VelMax); % 位置更新 pop(i).position = pop(i).position + pop(i).velocity; isBelow = pop(i).position < lb; isAbove = pop(i).position > ub; pop(i).velocity(isBelow) = -0.5 * pop(i).velocity(isBelow); pop(i).velocity(isAbove) = -0.5 * pop(i).velocity(isAbove); pop(i).position = max(min(pop(i).position, ub), lb); % 适应度计算 [cost, constr] = obj_func(pop(i).position); dynCoef = 1 - iter/max_iter; if any(constr < p.Results.min_YueShuTiaoJian | constr > p.Results.max_YueShuTiaoJian) penalty = sum(p.Results.pCoefYueShuTiaoJian .* dynCoef .* ... (constr - clamp(constr, p.Results.min_YueShuTiaoJian, p.Results.max_YueShuTiaoJian)).^2); cost = cost + penalty; end % 更新最优解 if cost < pop(i).best.cost pop(i).best.position = pop(i).position; pop(i).best.cost = cost; pop(i).best.Constr = constr; if cost < global_best.cost global_best = pop(i).best; end end end % 记录结果 convergence_curve(iter) = global_best.cost; BestPositions(iter,:) = global_best.position; % 早停检测 if iter>50 && std(convergence_curve(iter-20:iter))<1e-6 convergence_curve = convergence_curve(1:iter); BestPositions = BestPositions(1:iter,:); break; end end % 分配最终输出值 global_best_val = global_best.cost; % 关键修正点 convergence_curve = convergence_curve(1:iter); end function x = clamp(x, lb, ub) x = max(min(x, ub), lb); end优化这段代码
最新发布
06-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值