P10698 [SNCPC2024] 最大流

题意

给一个 \(n\) 个点 \(m\) 条边的 DAG,起点为 \(1\),终点不定,容量全为 \(1\)。再给定一个常数 \(k\)。设从 \(1\) 到 \(i\) 的最大流是 \(f_i\),对所有的 \(i\in[2,n]\) 求出 \(\min(f_i,k)\)。

\(n\le 10^5,m\le 2\times 10^5,k\le \min(50,n-1)\)。

思路

只有一个终点就可以直接跑网络流。

最大流就是问最多有多少路径满足起点在 \(S\),终点在 \(T\),没有交边。

将边转化成点,称之为假点,原来的点叫做真点。

问题变成给你一个起点的假点集合 \(A\),即真点起点的出边集合,和一个终点的假点集合 \(B\),即真点终点的入边集合。问最多的没有交点的路径条数,和 \(k\) 取 \(\min\)。


容易想到 LGV 引理。LGV 引理可以求所有划分不相交路径方式的贡献之和。一条路径的贡献定义为路径所有边的边权之积。

经典的套路,要知道最多有多少条不交的路径,就是随机赋边权,然后求 LGV 引理的矩阵 \(M\) 的秩。

具体地,\(M_{i,j}\) 定义为从 \(A_i\) 到 \(B_j\) 所有路径权值之和。


目前 \(|A|\) 和 \(|B|\) 都是 \(O(n)\) 的。

答案对 \(k\) 取 \(\min\)。

起点集合太大的情况,我们可以建 \(k\) 个虚的真点,与 \(1\) 相连。

然后我们就只有 \(k\) 个起点了,\(M\) 是 \(n\times k\) 的。

考虑按着 DAG 的拓扑序扫描终点集合 \(B\)。处理到真点 \(u\) 时,新的终点集合 \(B'\),对于其中一个假点 \(B_i'\),我们已知与它相邻的拓扑序较小的那个点的矩阵 \(M\),有 \(M_i' = \sum M_j w_{i,j}\),其中 \(w\) 是我们随机赋的边权。

然后你惊奇地发现,因为 \(w\) 是随机的,所以对于线性相关的 \(M_x,M_y\),你只需要加其中一个就可以了,因为总是存在随机 \(w\) 的方式使得某种随机权值下加两个的效果等同于另一种随机权值下加一个的效果。

因此我们对 \(M\) 的行向量求线性基。然后线性基的大小是 \(k \times k\) 的。

因此对于每条真边,都要做一次 \(O(k^2)\) 的转移,对每个真点得到一个 \(O(nk)\) 的矩阵,还要对矩阵求线性基,是 \(O(nk^2)\) 的。

因此总时间复杂度 \(O((n+m)k^2)\)。给了 \(15s\),包过的吧。

code

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace proficient {
constexpr int N=1e5+7,M=2e5+7,mod=1e9+7;
mt19937 rd(random_device{}());
int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
void _add(int &a,int b) { a=add(a,b); }
int mul(int a,int b) { return 1ll*a*b%mod; }
void _mul(int &a,int b) { a=mul(a,b); }
int ksm(int a,int b=mod-2) {
int s=1;
while(b) {
if(b&1) _mul(s,a);
_mul(a,a);
b>>=1;
}
return s;
}
int n,m,k;
int u,v;
vector<int> fr[N],tp,to[N];
int in[N];
int ans[N];
struct node {
int x[50];
int &operator [] (int pos) { return x[pos-1]; }
void clear() { memset(x,0,sizeof(x)); }
}a;
node operator * (node x,const int y) {
rep(i,1,k) _mul(x[i],y);
return x;
}
node operator += (node &x,node y) {
rep(i,1,k) _add(x[i],y[i]);
return x;
}
node operator -= (node &x,node y) {
rep(i,1,k) _add(x[i],mod-y[i]);
return x;
}
struct xxj {
node x[50];
node &operator [] (int pos) { return x[pos-1]; }
void insert(node y) {
rep(i,1,k) {
if(!y[i]) continue;
if(x[i-1][i]) {
int d=mul(y[i],ksm(x[i-1][i]));
y-=x[i-1]*d;
}else {
x[i-1]=y;
return;
}
}
}
int size() {
int cnt=0;
rep(i,1,k) if(x[i-1][i]) ++cnt;
return cnt;
}
}b[N+50];
void main() {
sf("%d%d%d",&n,&m,&k);
rep(i,1,m) {
sf("%d%d",&u,&v);
in[v]++;
fr[v].push_back(u);
to[u].push_back(v);
}
queue<int> q;
rep(i,1,n) if(!in[i]) q.push(i);
while(!q.empty()) {
int u=q.front();
tp.push_back(u);
q.pop();
for(int v : to[u]) if(!(--in[v])) q.push(v);
}
rep(i,1,k) {
// e[i+m]={i+n,1};
fr[1].push_back(i+n);
a.clear();
a[i]=1;
b[i+n].insert(a);
}
for(int u : tp) {
int it=0;
for(int v : fr[u]) {
++it;
a.clear();
rep(i,1,k) {
int w=abs((int)rd())%mod;
if(b[v][i][i]) a+=b[v][i]*w;
}
b[u].insert(a);
}
ans[u]=b[u].size();
}
rep(i,2,n) pf("%d ",ans[i]);
}
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
proficient :: main();
}

P10698 [SNCPC2024] 最大流的更多相关文章

  1. 使用C#处理基于比特流的数据

    使用C#处理基于比特流的数据 0x00 起因 最近需要处理一些基于比特流的数据,计算机处理数据一般都是以byte(8bit)为单位的,使用BinaryReader读取的数据也是如此,即使读取bool型 ...

  2. HTML 事件(三) 事件流与事件委托

    本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...

  3. FILE文件流的中fopen、fread、fseek、fclose的使用

    FILE文件流用于对文件的快速操作,主要的操作函数有fopen.fseek.fread.fclose,在对文件结构比较清楚时使用这几个函数会比较快捷的得到文件中具体位置的数据,提取对我们有用的信息,满 ...

  4. java.IO输入输出流:过滤流:buffer流和data流

    java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...

  5. java 字节流与字符流的区别

    字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢?实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作 ...

  6. BZOJ 3504: [Cqoi2014]危桥 [最大流]

    3504: [Cqoi2014]危桥 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1407  Solved: 703[Submit][Status] ...

  7. java I/O流

    输入流(读取数据的流) BufferedInputStream---继承--->FileInputStream--继承--->InputStream------> (1)字节流操作中 ...

  8. Ford-Fulkerson 最大流算法

    流网络(Flow Networks)指的是一个有向图 G = (V, E),其中每条边 (u, v) ∈ E 均有一非负容量 c(u, v) ≥ 0.如果 (u, v) ∉ E 则可以规定 c(u, ...

  9. .NET基础拾遗(3)字符串、集合和流

    Index: (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基础 ...

  10. C#开源实现MJPEG流传输

    本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. 许久以前写了篇文章<基于.NET打造IP智能网络视频监控系统>,记录和介绍了自己几年来积 ...

随机推荐

  1. leetcode 1406

    简介 国服第一的刷题视频 参考链接 https://www.bilibili.com/video/BV1W54y197NM?from=search&seid=16875469481128889 ...

  2. [原创]《C#高级GDI+实战:从零开发一个流程图》第07章:来吧,自定义“画布”控件!

    一.前言 上节课已经抽象出来了形状和连线,但是没解决程序复用的问题:现在所有的代码是写在窗口中的,如果想在其它程序想实现流程图,只能重新写代码或者复制粘贴代码,没办法简单复用,而且也无法保证功能的完整 ...

  3. API 网关在iPaaS集成平台中的功能具体体现

    前言 在数字化转型的浪潮中,企业纷纷加快转型步伐,应用程序与服务的集成需求呈现快速增长的趋势.然而,如何高效整合分散的资源,实现系统间的无缝协作,成为企业亟待解决的关键问题.API网关作为集成平台的核 ...

  4. Edu-Linguistic-English-IPA-英语发音:Vocal anatomy, muscles and function

    https://web.uvic.ca/ling/resources/ipa/charts/IPAlab/IPAlab.htm https://www.internationalphoneticass ...

  5. jar-protect 代码加壳加密工具【开源】

    开源地址:https://gitee.com/chejiangyi/jar-protect 介绍 java 本身是开放性极强的语言,代码也容易被反编译,没有语言层面的一些常规保护机制,jar包很容易被 ...

  6. zephyr学习(立创实战派开发板): 2. zephyr 点灯+串口通信

    配置好了zephyr 工作环境就可以开始自己的项目开发了. 参考:应用程序开发 - Zephyr Project Documentation --- Application Development - ...

  7. 2025熵密杯 -- 初始谜题 -- Reproducibility

    2025熵密杯 -- 初始谜题 -- Reproducibility 前言 本文记录2025熵密杯初始谜题赛题复现过程,参考languag3师傅的熵密杯题解博客.膜拜大佬~ https://langu ...

  8. 深度学习入门 (1)numpy+matplotlib

    导入numpy 和 matplotlib import numpy as np import matplotlib.pyplot as plt sinx函数图像 x = np.arange(0,6,0 ...

  9. D - Problem D. Euler Function HDU - 6322

    In number theory, Euler's totient function φ(n)φ(n) counts the positive integers up to a given integ ...

  10. ntp时间同步服务详解

    介绍 NTP属于运用层协议(依据UDP传输,运用的端口号为123),用来同步网络中分布式时间服务器和客户端之间的时间,使网络中的设备供应依据一起时间的运用成为可能. 时间服务器和客户端是相对的.供应时 ...