[JZOJ5511] 送你一个DAG

题目描述:

给出一个 \(n\) 个点 \(m\) 条边的 \(DAG\) 和参数 \(k\)。
定义一条经过 \(l\) 条边的路径的权值为 \(l_k\).
对于 \(i = 1…n\), 求出所有 \(1\) 到 \(i\) 的路径的权值之和, 对 \(998244353\) 取模.
对于前 \(20\)% 的数据, \(n ≤ 2000\),\(m ≤ 5000\);
对于另 \(10\)% 的数据, \(k = 1\);
对于另 \(20\)% 的数据, \(k ≤ 30\);
对于 \(100\)% 的数据, \(1 ≤ n ≤ 100000\),\(1 ≤ m ≤ 200000\),\(0 ≤ k ≤ 500\),
保证从 \(1\) 出发可以到达每个点, 可能会有重边。

题目解法:

前\(50\)%直接二项式定理,时间复杂度为\(O(nk^2)\)。
对于每一个点 \(i\),题目需要求的是:
\(Ans_i = \sum len(i)^k\)
根据组合数学,有:
\[n^m = \sum_{j=1}^{min(n,m)}S_m^j*\prod_{n-j+1}^n =\sum_{j=1}^nS_m^j*{j!}C_n^j\]
其中\(S_n^m\)为斯特林数,具体含义见这里
其实就是先选择\(j\)个盒子,然后把\(m\)个小球放到这\(j\)个盒子中。
那么带入本题中:
\(Ans_i = \sum len(i)^k = \sum\sum_{j=1}^{min(k,len(i))}S_k^j*{j!}*C_{len(i)}^j\)
然后是关键的一步:
\[\sum \sum_{j=1}^{k}S_k^j*{j!}*C_{len(i)}^j = \sum_{j=1}^kS^j_k*{j!} \sum C_{len(i)}^j\]
注意到这里把组合数\(C_{len(i)}^j\)提了出来,那么其实就是分离了变量\(len(i)\)
这个式子的前面部分中的\(S_k^j*{j!}\)可以直接算,所以只要处理组合数即可。
令\(F_{u,j} = \sum C_{len(u)}^j\)。
那么考虑由\(u\)转移到\(v\),那么即为:\(C_{len(i)}^j\) 变为 \(C_{len(i)+1}^j\)
由组合数公式可以知道:\(C_n^m = C_{n-1}^{m-1}+C_{n-1}^{m}\)
所以\(F_{i,j}\)的转移为:$ F_{v,j} = F_{u,j-1} + F_{u,j}。$
在\(TopSort\)时跑这个\(DP\),做出\(F_{i,j}\),然后带入上面的公式中即可计算\(Ans_i\)了。
总的时间复杂度为\(O(nk)\),可以跑过所有数据点。

实现代码

#include<bits/stdc++.h>
#define IL inline
#define ll long long
#define RG register
#define mod 998244353
using namespace std;
IL int gi(){
    RG int date = 0, m = 1;  RG char ch = 0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch = getchar();
    if(ch == '-'){m = -1; ch = getchar();}
    while(ch>='0'&&ch<='9'){date=date*10+ch-'0';ch=getchar();}
    return date*m;
}

int n,m,k,init[100005];
ll f[100005][505] , g[505][505] , fac , ans[100005];
struct Road{int to,next;}t[300005]; int head[100005],cnt;
queue<int>Q;

int main(){
    n = gi(); m = gi(); k = gi();
    for(int i = 1; i <= m; i++){
        int u = gi() , v = gi();
        t[++cnt] = (Road){v,head[u]}; head[u] = cnt;
        init[v] ++;
    }
    while(!Q.empty())Q.pop();
    for(int i = 1; i <= n; i ++)if(!init[i]){f[i][0] = 1; Q.push(i);}
    g[0][0] = 1;      //g[i][j] 把i个有异球 放到 j个无异盒 里。
    for(int i = 1; i <= k; i ++)
        for(int j = 0; j <= i; j ++){
            g[i][j] = g[i][j] + 1ll*g[i-1][j]*j%mod;
            if(j)g[i][j] = (g[i][j] + g[i-1][j-1])%mod;
        }
    while(!Q.empty()){
        RG int u = Q.front(); Q.pop();
        for(int i = head[u]; i; i = t[i].next){
            int v = t[i].to;
            init[v] --; if(!init[v])Q.push(v);
            for(int j = 0; j <= k; j ++){
                if(j)f[v][j] = (f[v][j] + f[u][j-1])%mod;
                f[v][j] = (f[v][j] + f[u][j]) %mod;

            }
        }
    }
    for(int i = 1; i <= n; i ++){
        fac = 1; ans[i] = 0;
        for(ll j = 1; j <= k; j ++){
            fac = 1ll*fac*j%mod; if(fac>=mod)fac%=mod;
            ans[i] = (ans[i] + 1ll*fac*g[k][j]%mod*f[i][j]) %mod;
        }
    }
    for(int i = 1; i <= n; i ++)printf("%lld\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/GuessYCB/p/8260850.html

时间: 2024-11-13 07:52:04

[JZOJ5511] 送你一个DAG的相关文章

送你一个薰衣草枕头

“真理是什么?现实主义认为在我们之外存在着一个外在的世界,那个世界不为我们的意志所改变,寻求那个世界真相的努力,渐渐成为物理学.化学.这些科学都试图用不同的方式去建立模型,来解释我们这个世界和那个世界的关系.但是其实最后那个世界究竟是怎样,我们没有真相……” 哲学大课,老师在讲台旁低着头绕来绕去,盯着地板说天书,说的内容比他的步伐还要绕来绕去,张安迪回头看了看挂在教室门口上方的钟,这是她第四次回头看时间.终于五点半了,只要坚持一刻钟,课就结束了,这所谓的世界真相就和她没关系了. 窗外已经暗了下来

0408送队友一个汉堡包

经过上一次的结对合作,我对团队合作有了一定的认识.我们在结对合作的时候,不能一昧的只做自己的事,而是要跟队友们一起讨论,多听取队友的意见,同时在团队讨论的时候也多讲一下自己的观点. 由于我们两个人的编程能力并不是十分的好,所以在做任务的过程中遇到了重重困难,但是我们对完成任务有一颗坚决的心,一种良好的态度.经过结对合作,我看到了队友为我们项目所付出的巨大努力,他找了很多相关的资料供我参考,他那种认真,负责的态度,让我感到很感激. 从这次的结对合作中,队友的优点:1.对事情认真,负责: 2.体贴,

送队友一个汉堡包

我们队伍的编程能力不是很好,尤其是我不懂的地方比较多,所以在完成作业的过程中遇到很多很多的困难,经过这次合作,我希望大家能一起进步一起做汉堡 我对我同伴的看法,首先优点是: 1.做事认真  2.态度端正   3.正能量很多 4.有正义感 缺点是:过于沉默,有隐身消失的能力 总之以后记得一起做汉堡.

把你的变更推送到一个远程仓库

假设你对 “experimental”分支做了变更并且希望把他push到"origin"远程仓库中去. 你可以这样做: 1 git push origin experimental 你可能将会收到:远程仓库无法fast-forward该分支的错误信息, 这将意味着可能有别人push了不同的变更到了这个分支上.所以,你需要fetch和merge别人的变更并再次尝试push操作.

uva1201 DAG 最小路径覆盖,转化为 二分图

大白例题P356 你在一座城市里负责一个大型活动的接待工作.你需要去送m个人从出发地到目的地,已知每个人的出发时间出发地点,和目的地点,你的任务是用尽量少的出租车送他们,使得每次出租车接客人,至少能提前一分钟达到他所在的位置,城市为网格 (x1,y1) ===>(x2,y2) 需要|x1-x2|+|y1-y2|分钟 题解: 本题的模型是DAG的最小路径覆盖.所谓最小路径覆盖就是在图中找尽量少的路径,使得每个结点恰好在一条路径上(话句话说,不同路径不能有公共点).单独的节点也可以作为一条路径. 本

如何快速做一个山寨的实时“大数据”处理

前言为啥写这篇文章?因为我现在做的这套实时计算系统在公司里很难玩下去了.去年年初来到ctrip,主要就是做两个实时应用,一个是实时报警,功能是做出来了,但应用效果不好:一个是XXX(敏感应用,不敢写出来,以XXX代替),也是实现了功能需求,但想继续按自己的思路往下走是不可能了,我捉急的表达能力很难让上头去理解实时计算和传统的request-response方式的应用不同点在哪里,为啥要这么干,也很难明白上头喜欢的系统是什么样的,真是挠破头.我的方式看来是做不下去了,但总觉得还是有点价值,所以写下

UVALive-3126 Taxi Cab Scheme (DAG的最小路径覆盖)

题目大意:要给n个人安排车,已知每个人的出发时间和起点与终点,问最少需要安排几辆车才能完成任务. 题目分析:最小路径覆盖.如果送完a到目的地后能在b出发之前赶来接b,那么连一条有向边a->b,最终将得到一个DAG.最少路径覆盖数便是答案.解法:把所有节点 i 拆成 i 和 i’,如果 i 和 j 之间连有一条边,那么则在二分图中连接 i->j’.最少路径覆盖数便是 n-最大匹配数. 代码如下: # include<iostream> # include<cstdio>

【LA3126 训练指南】出租车 【DAG最小路径覆盖】

题意 你在一座城市里负责一个大型活动的接待工作.明天将有m位客人从城市的不同的位置出发,到达他们各自的目的地.已知每个人的出发时间,出发地点和目的地.你的任务是用尽量少的出租车送他们,使得每次出租车接客人时,至少能提前一分钟到达他所在的位置.注意,为了满足这一条件,要么这位客人是这辆出租车接送的第一个人,要么在接送完上一个客人后,有足够的时间从上一个目的地开到这里. 为了简单起见,假定城区是网格型的,地址用坐标(x,y)表示,出租车从(x1,y1)到(x2,y2)处需要行驶|x1-x2|+|y1

关于极光推送Jpush的demo

关于极光推送Jpush 推送是手机app必不可少的一样功能,这次由于公司项目需要研究了一下.由于推送一般写于服务端,所以对于不会Android的javaweb程序员要写出一个完整的demo是一件很头痛的事情.所以我就在这里从头到尾写一个例子以示参考.由于我也不懂Android 只是由于项目需要百度了一个demo,当中有很多不足的地方忘各位大神指正. 一.首先先简单的介绍一下什么是极光推送 ①为什么需要推送:为了解决数据同步的问题,在手机平台上,常用的方法有2种.一种是定时去服务器上查询数据,也叫