ZOJ 3408 Gao
ZOJ题目页面传送门
给定一个有向图\(G=(V,E),|V|=n,|E|=m\)(可能有重边和自环,节点从\(0\)开始编号),以及\(q\)组询问,对于每组询问你需要回答有多少条从节点\(0\)开始的最短路经过节点\(x\)(节点\(0\)到某一个节点的最短路可能不唯一),输出答案的后\(10\)位。本题多测。
\(q,n\in\left[1,10^4\right],m\in\left[1,5\times10^4\right]\),边权为正。
既然题目要数最短路的个数,我们可以把那些不在最短路上的边去掉,只保留在最短路上的边,构造出一个新图\(G'=(V,E')\)。这样问题就转化成了在\(G'\)上有多少条从节点\(0\)开始的路径经过节点\(x\)(显然的吧)。那怎么知道那些边该留那些边不该留呢?我们可以先跑一遍堆优化Dijkstra求出到节点\(0\)的最短路长度数组\(dis\),那么\(E'=\{(x,y,len)\mid(x,y,len)\in E,dis_y=dis_x+len\}\)。
接下来我们就要求在\(G'\)上有多少条从节点\(0\)开始的路径经过节点\(x\)了。我们考虑将一条从节点\(0\)开始、经过节点\(x\)、终点为\(y\)的路径拆成两段:\(0\to x,x\to y\),对它们分别计数,最后相乘即可。很显然,\(G'\)是无环的,也就是一个DAG(因为边权为正),这样就可以在\(G'\)上DP了。设\(dp1_i\)表示路径\(0\to i\)的个数,\(dp2_i\)表示以\(i\)为起点的路径的个数(即\(\sum\limits_{j\in V}\text{路径}i\to j\text{的个数}\))。状态转移方程显然是:
dp1_i&=\begin{cases}1&i=0\\\sum\limits_{(j,i,len)\in E'}dp1_j&i>0\end{cases}\\
dp2_i&=\sum_{(i,j,len)\in E'}dp2_j+1
\end{aligned}
\]
可这是一个图啊,DP顺序是什么呢?容易发现,要想知道\(dp1_i\),得先知道\(\forall j\left((j,i,len)\in E'\right),dp1_j\),所以要按照拓扑序DP;\(dp2\)反之,要按照拓扑逆序DP。(如果你懒得写拓扑排序,也可以记忆化搜索)
这题还有个毒瘤的地方,就是要保留后\(10\)位(即\(\bmod 10^{10}\)),在乘法的时候会到\(10^{20}\),爆unsigned long long。这时聪(rui)明(zhi)的读者可能会写高精度(毕竟只有最后一步才是乘法,不会TLE),但有个更巧妙的方法:利用乘法分配律,显然
\]
这样最大是\(10^5\times10^{10}=10^{15}\)级别的,不会爆。
下面贴AC代码:(最近码风有了很大的改变)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int mod=10000000000ll,inf=1e18;
const int N=10000;
int n/*点数*/,m/*边数*/,qu/*数据组数*/;
vector<pair<int,int> > nei[N]/*原邻接表*/;
vector<int> dnei[N]/*新邻接表*/,rnei[N]/*新反邻接表*/;
int dis[N]/*到节点0的最短路长度*/;
bool vis[N]/*访问标记*/;
int ideg[N]/*新图中的入度*/;
void dijkstra(){//堆优化Dijkstra
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
q.push(mp(0,0));
for(int i=1;i<n;i++)dis[i]=inf;
for(int i=0;i<n;i++)vis[i]=false;
while(q.size()){
int x=q.top().Y;
q.pop();
if(vis[x])continue;
vis[x]=true;
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i].X,len=nei[x][i].Y;
if(dis[x]+len<dis[y])q.push(mp(dis[y]=dis[x]+len,y));
}
}
//建新图
for(int i=0;i<n;i++)dnei[i].clear(),rnei[i].clear();
for(int i=0;i<n;i++)for(int j=0;j<nei[i].size();j++){
int x=nei[i][j].X,len=nei[i][j].Y;
if(dis[x]==dis[i]+len)dnei[i].pb(x),rnei[x].pb(i),ideg[x]++;
}
}
vector<int> topo;//拓扑序
void toposort(){//拓扑排序
queue<int> q;
for(int i=0;i<n;i++)if(!ideg[i])q.push(i);
topo.clear();
while(q.size()){
int x=q.front();
q.pop();
topo.pb(x);
for(int i=0;i<dnei[x].size();i++){
int y=dnei[x][i];
ideg[y]--;
if(!ideg[y])q.push(y);
}
}
}
int dp1[N]/*dp1[i]表示新图中路径0->i的个数*/,dp2[N]/*dp2[i]表示新图中路径i->j的个数之和*/;
int prod(int x,int y){//mod 10^5意义下的乘法
int lx=x%100000,ly=y%100000;
return (lx*y+(x-lx)*ly)%mod;
}
void prt(int x){//输出后10位
vector<int> v;
for(int i=1;i<=10;i++)v.pb(x%10),x/=10;
for(int i=9;~i;i--)printf("%lld",v[i]);
}
void mian(){
for(int i=0;i<n;i++)nei[i].clear();
while(m--){
int x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
nei[x].pb(mp(y,z));
}
dijkstra();
toposort();
for(int i=0;i<n;i++){//dp1,拓扑序
int x=topo[i];
dp1[x]=!x;
for(int j=0;j<rnei[x].size();j++)
(dp1[x]+=dp1[rnei[x][j]])%=mod;
}
for(int i=n-1;~i;i--){//dp2,拓扑逆序
int x=topo[i];
dp2[x]=1;
for(int j=0;j<dnei[x].size();j++)
(dp2[x]+=dp2[dnei[x][j]])%=mod;
}
while(qu--){
int x;
scanf("%lld",&x);
prt(prod(dp1[x],dp2[x]));/*相乘得到总数*/puts("");
}
}
signed main(){
// freopen("C:\\Users\\chenx\\OneDrive\\桌面\\txt.txt","r",stdin);
while(~scanf("%lld%lld%lld",&n,&m,&qu))mian();
return 0;
}
我做这题的时候打错了一个字母,把prod(int,int)函数里的ly=y%100000打成了ly=x%100000,导致我调了几个小时。。。哎,血的教训!
ZOJ 3408 Gao的更多相关文章
- zoj 3672 Gao The Sequence
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4915题意:a[k]-一个任意的数,这个数要等于a[1]~a[k]每个数减去任意 ...
- ZOJ 3647 Gao the Grid dp,思路,格中取同一行的三点,经典 难度:3
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4837 三角形的总数=格子中任取3个点的组合数-同一横行任取3个点数目-同一纵行 ...
- zoj 3647 Gao the Grid
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4837 先求出从所有点随机找出三个点的组合数,然后去掉共线的,平行好去掉,斜线就 ...
- Zoj 3535 Gao the String II (AC自己主动机+dp)
题目大意: 用集合A中的串构造出一个串,使之让很多其它的setB中的串成为他的子串. 思路分析: 和 Codeforces 86C 几乎相同. 只是这里是要用A中的构造. 先用A 和 B的串构造一个自 ...
- ZOJ Monthly, November 2012
A.ZOJ 3666 Alice and Bob 组合博弈,SG函数应用 #include<vector> #include<cstdio> #include<cstri ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
- zoj Gao The Sequence
Gao The Sequence Time Limit: 2 Seconds Memory Limit: 65536 KB You are given a sequence of integ ...
- Zoj 3781(构造)
Zoj 3781(构造) Zoj 3781 As we all know, Coach Gao is a talented chef, because he is able to cook M dis ...
- ZOJ People Counting
第十三届浙江省大学生程序设计竞赛 I 题, 一道模拟题. ZOJ 3944http://www.icpc.moe/onlinejudge/showProblem.do?problemCode=394 ...
随机推荐
- 使用wincc C脚本查找窗口句柄的方法
关于窗口句柄的用法,网上可以搜到很多相关的文章,本文以windows API接口函数为例,简单介绍一下基本使用,主要包括找到此窗体,在找到的窗体写入数据,对窗体进行关闭,最大化,最小化的操作: 1.利 ...
- 硬件设计--DC/DC电源芯片详解
本文参考:http://www.elecfans.com/article/83/116/2018/20180207631874.html https://blog.csdn.net/wangdapao ...
- 洛谷P3275 [SCOI2011]糖果 题解
题目链接: https://www.luogu.org/problemnew/show/P3275 分析: 本题就是一个裸的差分约束. 核心: x=1x=1x=1时,a=b,a−>b,b−> ...
- C#3.0新增功能07 查询表达式
连载目录 [已更新最新开发文章,点击查看详细] 查询是什么及其作用是什么 查询是一组指令,描述要从给定数据源(或源)检索的数据以及返回的数据应具有的形状和组织. 查询与它生成的结果不同. 通常情 ...
- [leetcode] 113. Path Sum II (Medium)
原题链接 子母题 112 Path Sum 跟112多了一点就是保存路径 依然用dfs,多了两个vector保存路径 Runtime: 16 ms, faster than 16.09% of C++ ...
- python红蓝英雄大乱斗(面向对象实现)
红蓝英雄大乱斗 游戏规则 ''' 有红蓝两方英雄(可自定义个数) 随机一方英雄使用随机攻击方式攻击另一方英雄,任意一方英雄全部阵亡则游戏结束 每个英雄有 名字.生命值.普通攻击.Q技能攻击.W技能攻击 ...
- 《C# 语言学习笔记》——委托
委托是一种可以把引用存储为函数的类型. 委托的声明非常类似于函数,但不带函数体,且要使用delegate关键字.委托的声明制定了一个返回类型和一个参数列表. 在定义了委托后,就可以声明该委托类型的变量 ...
- Spring方法级别数据校验:@Validated + MethodValidationPostProcessor
每篇一句 在<深度工作>中作者提出这么一个公式:高质量产出=时间*专注度.所以高质量的产出不是靠时间熬出来的,而是效率为王 相关阅读 [小家Java]深入了解数据校验:Java Bean ...
- jsp的简介(1)
一.什么是Java Server Pages? JSP全称Java Server Pages,是一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%&g ...
- SSM-员工管理系统Demo---带分页和校验(含源码)
页面展示: 前端JSP: <%@ page language="java" contentType="text/html; charset=UTF-8" ...