In mathematics you don't understand things, you just get used to them.

分类 笔记 下的文章

Description

Link.

给定 $n$ 组坐标。构造长度为 $m$ 的序列 $\{c_n\}$ 和 $n$ 组包含 LRUD 的路径,满足对于每一组坐标:

  • $c_i$ 表示第 $i$ 步「步长」。
  • 对于每个坐标,从 $(0,0)$ 开始走,共走 $m$ 步。第 $i$ 步可以让 $(x,y)$ 变成 $(x±c_i,y)$ 或 $(x,y±c_i)$ 。
  • 走完 $m$ 次之后,恰好走到这组坐标。
  • 要求 $m\leq 40,c_i\leq 10^{12}$ 。

Solution

好强的题啊。

先考虑无解的情况。

即是 $x_{i}+y_{i}$ 的奇偶性不同的情况为无解。

仔细看 $m$ 的限制疑似是 $\log(x+y)$ 级别的,考虑二进制拆分。

于是考虑 $\{2^{k}\}$ 可以凑出的坐标。

只考虑 1-dimension 的做法。

我们能够维护的地方就是 $\sum_{i=0}^{k}2^{i}=2^{k+1}-1$(这里算的是曼哈顿距离)。

那么这一定是个奇数,如果 $(x,y)$ 的曼哈顿距离是偶数就考虑换原点。

那么这就做完了。

full ver.

using i64 = long long;
using pii = std::pair<i64, i64>;

std::vector<int> sL;
std::vector<std::string> dR;
std::pair<int, int> as[MAXN];
int n, wax[4], way[4];
char trans[4];

int main () {
    std::ios::sync_with_stdio ( 0 ); std::cin.tie ( 0 ); std::cout.tie ( 0 );
    std::cin >> n; initial ();
    rep ( i, 1, n )    std::cin >> as[i].first >> as[i].second;
    rep ( i, 2, n ) if ( ( as[i].first + as[i].second + as[i - 1].first + as[i - 1].second ) & 1 )    return ( puts ( "-1" ), 0 );
    sL.push_back ( 1 );
    rep ( i, 1, 30 )    sL.push_back ( 1 << i );
    if ( ( ( as[1].first + as[1].second ) & 1 ) ^ 1 )    sL.push_back ( 1 );
    std::reverse ( sL.begin (), sL.end () );
    rep ( i, 1, n ) {
        dR.push_back ( std::string () );
        i64 curx = as[i].first, cury = as[i].second;
        if ( ( ( curx + cury ) & 1 ) ^ 1 )    dR[i - 1].push_back ( 'U' ), cury --;
        per ( j, 30, 0 ) rep ( k, 0, 3 ) {
            i64 nxtx = curx + ( i64 )wax[k] * ( ONE64 << j );
            i64 nxty = cury + ( i64 )way[k] * ( ONE64 << j );
            if ( std::abs ( nxtx ) + std::abs ( nxty ) < ( ONE64 << j ) ) {
                curx = nxtx, cury = nxty;
                dR[i - 1].push_back ( trans[k] );
                break;
            }
        }
    }
    std::cout << sL.size () << '\n';
    for ( int p : sL )    std::cout << p << ' ';
    std::cout << '\n';
    for ( std::string p : dR )    std::cout << p << '\n';
    return 0;
}

Desciption

Link.

给定一个值域在 $[1,x]$ 的长度为 $n$ 的序列(由随机数构成),求给定一组区间中的最小值的最大值的期望。

Solution

记:

$$ w=\max\{\min\{a_{l_{j}},a_{l_{j}+1},\cdots,a_{r_{j}}\}|j\in[1,q]\} $$

因为我们最后取的是 $\max$,不能直接用全概率公式,转化一下:

$$ E(w)=\sum_{i=0}^{\infty}P(w\ge i)=\sum_{i=0}^{\infty}1-P(w<i) $$

这意味着每一个被询问区间中的最小值都需 $<i$。也就是说,每一个区间至少需要一个 $<i$ 的数。

这对于每一个区间来说概率为 $\frac{i-1}{x}$。又因为区间可能出现相交,所以我们考虑用点去被包含于区间。

当然,一个区间包含另一个区间,这个区间肯定是没有用的。然后把区间按左右端点分别为第一、第二关键字排序。

枚举 $w$,设 $f_{i}$ 表示区间右端点在 $i$ 之前的所有区间满足条件的概率。

$$ f_{i}=\frac{w-1}{x}\times\sum_{j=0}^{i}f_{j}\times(1-\frac{w-1}{x})^{i-j-1} $$

#include <cstdio>

using i64 = long long;

const int MOD = 666623333;
const int MAXN = 2e3 + 5;

int n, x, q, ar[MAXN];
i64 f[MAXN][2], ff[MAXN][2];

void imax ( int& a, const int b ) { a = a < b ? b : a; }
int add ( const int a, const int b, const int p = MOD ) { return a + b < p ? a + b : ( a + b ) % p; }
int sub ( const int a, const int b, const int p = MOD ) { return a - b < 0 ? a - b + p : a - b; }
int mul ( const i64 a, const i64 b, const int p = MOD ) { return a * b % p; }
int cpow ( int bas, int idx = MOD - 2 ) {
    int res = 1;
    while ( idx ) {
        if ( idx & 1 )    res = mul ( res, bas );
        bas = mul ( bas, bas ), idx >>= 1;
    }
    return res % MOD;
}

int main () {
    scanf ( "%d%d%d", &n, &x, &q );
    for ( int i = 1, tmpl, tmpr; i <= q; ++ i )    scanf ( "%d%d", &tmpl, &tmpr ), imax ( ar[tmpr + 1], tmpl );
    for ( int i = 1; i <= n + 1; ++ i )    imax ( ar[i], ar[i - 1] );
    i64 ix = cpow ( x ), ans = 0;
    for ( int i = 1; i <= x; ++ i ) {
        i64 p = mul ( i - 1, ix ) % MOD, ip = cpow ( 1 - p ), s;
        ff[0][0] = ff[0][1] = 1;
        for ( int j = 1; j <= n; ++ j )    ff[j][0] = mul ( ff[j - 1][0], 1 - p ) % MOD, ff[j][1] = mul ( ff[j - 1][1], ip ) % MOD;
        f[0][0] = 0, f[0][1] = 1;
        for ( int j = 1; j <= n; ++ j ) {
            f[j][0] = mul ( mul ( p, sub ( f[j - 1][1], ar[j] ? f[ar[j] - 1][1] : 0 ) ) % MOD, ff[j - 1][0] ) % MOD;
            f[j][1] = add ( mul ( f[j][0], ff[j][1] ) % MOD, f[j - 1][1] ) % MOD;
        }
        s = 0;
        for ( int j = ar[n + 1]; j <= n; ++ j )    s = add ( s, mul ( f[j][0], ff[n - j][0] ) % MOD ) % MOD;
        ans = sub ( add ( ans, 1 ) % MOD, s );
    }
    printf ( "%lld\n", ans % MOD );
    return 0;
}

Description

Link.

给一个树,$n$ 个点,有点权,初始根是 1。

$m$ 个操作,种类如下:

1 x 将树根换为 $x$。

2 x y 给出两个点 $x,y$,从 $x$ 的子树中选每一个点,$y$ 的子树中选每一个点,求点权相等的情况数。

Solution

我首先认为这是 SNOI2017 一个简单的询问 搬到树上。

我们传统地把此题分为两个 $\texttt{pass}$,一个询问,一个修改。

  • $\texttt{pass 1}$:询问

我直接按 一个简单的询问 的方法讲。其实是把以前的题解 copy 过来了

由于是出现次数,满足区间加减性,所以我们可以这样表达 $\mathrm{get}(l,r,x)$(省略 $x$):

$$ \mathrm{get}(l,r)=\mathrm{get}(1,r)-\mathrm{get}(1,l-1) $$

那么我们代进原式,化一波式子($\mathrm{get}(p)=\mathrm{get}(1,p,x)$):

$$ \sum_{i=1}^{\infty}\mathrm{get}(l_{1},r_{1},x)\times\mathrm{get}(l_{2},r_{2},x) $$

$$ \sum_{i=1}^{\infty}(\mathrm{get}(1,r_{1})-\mathrm{get}(1,l_{1}-1))\times(\mathrm{get}(1,r_{2})-\mathrm{get}(1,l_{2}-1)) $$

$$ \sum_{i=1}^{\infty}\mathrm{get}(r_{1})\times\mathrm{get}(r_{2})-\mathrm{get}(r_{1})\times\mathrm{get}(l_{2}-1)-\mathrm{get}(l_{1}-1)\times\mathrm{get}(r_{2})+\mathrm{get}(l_{1}-1))\times\mathrm{get}(l_{2}-1) $$

$$ \mathrm{let}\ F(x,y)=\sum_{d}\mathrm{get}(1,l,d)\times\mathrm{get}(1,r,d) $$

则答案为:

$$ F(r_{1},r_{2})-F(r_{1},l_{2}-1)-F(l_{1}-1,r_{2})+F(l_{1}-1,l_{2}-1) $$

考虑怎么更新,比如从 $l$ 更新到 $l+1$,则:

$$ \mathrm{get(1,l)}\times\mathrm{get}(1,r) $$

$$ \mathrm{get(1,l+1)}\times\mathrm{get}(1,r) $$

$$ \mathrm{get(1,l)}\times\mathrm{get}(1,r)+\mathrm{cont}(a_{l}) $$

其中 $\mathrm{cont}(a_{l})$ 表示 $a_{l}$ 的出现次数。

则我们就知道怎么更新了,由于我们维护和的是前缀信息,所以姿势和普通莫队有点不一样。

维护两个数组 cntl[x]cntr[y] 表示答案式子

$$ F(r_{1},r_{2})-F(r_{1},l_{2}-1)-F(l_{1}-1,r_{2})+F(l_{1}-1,l_{2}-1) $$

子树的话直接 DFS 序拍到序列上。

  • $\texttt{pass 2}$:修改

现在我们面临着查询操作我们是用莫队整的,但这个修改貌似不单纯。其实也是从树剖模板缝合过来的

分类讨论,设我们当前要换的根为 $rt$,现在来处理询问,设查询的节点为 $u$,$\text{LCA}(u,v)$ 为节点 $u$ 和节点 $v$ 的最近公共祖先。

    • 如果 $rt=u$,则我们直接对整棵树进行查询。
    • 如果 $\text{LCA}(u,rt)\neq u$,此时修改不影响查询。
    • 如果 $\text{LCA}(u,rt)=u$,此时 $rt$ 在 $u$ 的子树里,那么需要查询的地方就很明确了,后面的步骤显然。

于是我们不需要实际的去处理这个修改,然后就可以直接莫队了。

(整体感觉是个 原题+假上树+树剖模板 的缝合题)

/* Clearink */

#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int MAXN = 5e5 + 5, MAXM = 1e6 + 5;

int rint () {
    int x = 0, f = 1; char c = getchar ();
    for ( ; c < '0' || c > '9'; c = getchar () )    f = c == '-' ? -1 : f;
    for ( ; c >= '0' && c <= '9'; c = getchar () )    x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
    return x * f;
}

template<class _T>
void wint ( _T x ) {
    if ( x < 0 )    putchar ( '-' ), x = ~ x + 1;
    if ( x > 9 )    wint ( x / 10 );
    putchar ( x % 10 ^ '0' );
}

template<class _T> void swapp ( _T& x, _T& y ) { _T w = x; x = y; y = w; }

struct GraphSet {
    int to, nx;
    GraphSet () : to ( 0 ), nx ( 0 ) {}
    GraphSet ( const int a, const int b ) : to ( a ), nx ( b ) {}
} asg[MAXN * 2];

struct Quest {
    int l, r, ID, x;
    Quest () : l ( 0 ), r ( 0 ), ID ( 0 ), x ( 0 ) {}
    Quest ( const int a, const int b, const int c, const int d ) : l ( a ), r ( b ), ID ( c ), x ( d ) {}
} asq[MAXM * 8], itls[MAXN];

LL cur = 0, ans[MAXM], buc1[MAXN], buc2[MAXN];
int rt, pos[MAXN], blo = 320, col[MAXN], freq;
int n, m, bgn[MAXN], cnt, sjc, segl[MAXN], segr[MAXN], kfa[MAXN][21], a[MAXN], dept[MAXN], pri[MAXN], len;

void addE ( const int u, const int v ) { asg[++ cnt] = GraphSet ( v, bgn[u] ), bgn[u] = cnt; }
bool existcmp ( const Quest& one, const Quest& ano ) { return pos[one.l] == pos[ano.l] ? one.r < ano.r : one.l < ano.l; }

void dfs ( const int u, const int lst ) {
    kfa[u][0] = lst, dept[u] = dept[lst] + 1;
    segl[u] = ++ sjc, col[sjc] = a[u];
    for ( int i = 1; i <= 20; ++ i )    kfa[u][i] = kfa[kfa[u][i - 1]][i - 1];
    for ( int i = bgn[u]; i; i = asg[i].nx ) {
        int v = asg[i].to;
        if ( v == lst )    continue;
        dfs ( v, u );
    }
    segr[u] = sjc;
}

int calcKAC ( int u, int k ) {
    for ( int i = 20; ~ i; -- i ) {
        if ( k >= ( 1 << i ) )    k -= ( 1 << i ), u = kfa[u][i];
    }
    return u;
}

int calcLCA ( int u, int v ) {
    if ( dept[u] < dept[v] )    swapp ( u, v );
    for ( int i = 20; ~ i; -- i ) {
        if ( dept[kfa[u][i]] >= dept[v] )    u = kfa[u][i];
    }
    if ( u == v )    return u;
    for ( int i = 20; ~ i; -- i ) {
        if ( kfa[u][i] != kfa[v][i] )    u = kfa[u][i], v = kfa[v][i];
    }
    return kfa[u][0];
}

void initial () {
    for ( int i = 1; i <= n; ++ i )    pos[i] = ( i - 1 ) / blo + 1;
    sort ( pri + 1, pri + 1 + n );
    len = unique ( pri + 1, pri + 1 + n ) - pri - 1;
    for ( int i = 1; i <= n; ++ i )    a[i] = lower_bound ( pri + 1, pri + 1 + len, a[i] ) - pri;
    dfs ( 1, 0 );
}

void splitASdrug ( const int u, int& ils ) {
    if ( u == rt )    itls[++ ils] = Quest ( 1, n, 0, 0 );
    else {
        int lca = calcLCA ( u, rt );
        if ( lca != u )    itls[++ ils] = Quest ( segl[u], segr[u], 0, 0 );
        else {
            int ar = calcKAC ( rt, dept[rt] - dept[u] - 1 );
            if ( 1 <= segl[ar] - 1 )    itls[++ ils] = Quest ( 1, segl[ar] - 1, 0, 0 );
            if ( segr[ar] + 1 <= n )    itls[++ ils] = Quest ( segr[ar] + 1, n, 0, 0 );
        }
    }
}

void transASsub ( const int l1, const int r1, const int l2, const int r2, const int ID ) {
    asq[++ m] = Quest ( r1, r2, ID, 1 ), asq[++ m] = Quest ( r1, l2 - 1, ID, -1 );
    asq[++ m] = Quest ( l1 - 1, r2, ID, -1 ), asq[++ m] = Quest ( l1 - 1, l2 - 1, ID, 1 );
}

void transASmany ( const int l, const int r ) {
    ++ freq;
    int ils = 0; splitASdrug ( l, ils );
    int aim = ils; splitASdrug ( r, ils );
    for ( int i = 1; i <= aim; ++ i ) {
        for ( int j = aim + 1; j <= ils; ++ j )    transASsub ( itls[i].l, itls[i].r, itls[j].l, itls[j].r, freq );
    }
}

void add1 ( const int x ) { cur += buc2[col[x]], buc1[col[x]] ++; }
void add2 ( const int x ) { cur += buc1[col[x]], buc2[col[x]] ++; }
void sub1 ( const int x ) { cur -= buc2[col[x]], buc1[col[x]] --; }
void sub2 ( const int x ) { cur -= buc1[col[x]], buc2[col[x]] --; }
void captainMO () {
    int nowl = 0, nowr = 0;
    for ( int i = 1; i <= m; ++ i ) {
        for ( ; nowl < asq[i].l; add1 ( ++ nowl ) ) ;
        for ( ; nowr < asq[i].r; add2 ( ++ nowr ) ) ;
        for ( ; nowl > asq[i].l; sub1 ( nowl -- ) ) ;
        for ( ; nowr > asq[i].r; sub2 ( nowr -- ) ) ;
        ans[asq[i].ID] += cur * asq[i].x;
    }
}

int main () {
    n = rint (); int _waste_ = rint ();
    for ( int i = 1; i <= n; ++ i )    a[i] = pri[i] = rint ();
    for ( int i = 1; i < n; ++ i ) {
        int u = rint (), v = rint ();
        addE ( u, v ), addE ( v, u );
    }
    initial (), rt = 1;
    for ( int i = 1; i <= _waste_; ++ i ) {
        int c = rint (), x, y;
        if ( c == 1 )    rt = rint ();
        else    x = rint (), y = rint (), transASmany ( x, y );
    }
    sort ( asq + 1, asq + 1 + m, existcmp ), captainMO ();
    for ( int i = 1; i <= freq; ++ i )    wint ( ans[i] ), putchar ( '\n' );
    return 0;
}

基本没有严谨证明。

Part. 1 概念

Part. 1-1 流网络

流网络是一个有向图(不考虑反向边),我们把这个图记为 $G=(V,E)$。

其中有两个特殊的点 $s,t$,分别成为源点和汇点。

对于每一个 $(u,v)\in E$,我们给它两个属性,一个 $c(u,v)$,表示这条边的容量;一个 $f(u,v)$,表示这条边的流量。

每一条边都需要满足两个性质:

  • 容量限制,$0\le f(u,v)\le c(u,v)$。
  • 流量守恒,$\sum_{(x,u)\in E}f(x,u)=\sum_{u,y\in E}f(u,y)$,即流进来多少流出去多少。

(这里的定义是 yxc / 算导 的定义,和网络上大部分的资料不同)

我们称一个网络的可行流的流量为 $\left(\sum_{(s,x)\in E}f(s,x)\right)-\left(\sum_{(y,s)\in E}f(y,s)\right)$,即流出源点的流量减去流入源点的流量。

一个可行流的方案我们记为 $f$,其流量记为 $|f|$。

Part. 1-2 残量网络

残量网络即把原网络的反向边算进去,把反向边的边集记为 $E'$,把残量网络表示为 $G_{f}=(V,E\cup E')$,把残量网络的一个可行流方案记为 $f'$,同理 $|f'|$,$c_{f}(u,v)$,$f'(u,v)$。

这里定义 $f$ 之间的加法:

首先我们知道对于 $G$ 的每一个不同的 $f$ 而言,其 $G'$ 是不同的。

我们定义 $c'(u,v)=\begin{cases}c(u,v)-f(u,v),(u,v)\in E \\ \displaystyle f(v,u),(u,v)\in E'\end{cases}$。

那么 $f$ 之间的加法是这样定义的:$f'(u,v)=f'(u,v)+f(u,v),f'(v,u)=f'(v,u)-f(u,v)$。(定义不太清楚,自行意会吧)

然后可以证明 $|f+f'|=|f|+|f'|$,注意 $|f'|$ 可能为负。

Part. 1-3 增广路

增广路就是一条从 $s$ 到 $t$ 的简单路径,满足路上每条边的容量都 $>0$。

Part. 1-4 割

割是对 $V$ 进行的划分。

首先我们划分出两个集合 $S,T$,需满足 $s\in S,t\in T$ 且 $S\cup T=V,S\cap T=\empty$。

我们再定义割的容量:$c(S,T)=\sum_{u\in S}\sum_{v\in T}c(u,v)$(不考虑反向边)。

割的流量:$f(S,T)=\left(\sum_{u\in S}\sum_{v\in T}f(u,v)\right)-\left(\sum_{u\in S}\sum_{v\in T}f(v,u)\right)$。

这里流量和容量的定义不对称,不过 不 影 响。

可以证明对于任意可行流的任意割,有 $f(S,T)\le c(S,T)$,也有 $|f|=f(S,T)\le c(S,T)$。

最小割指容量最小的割。

Part. 1-5 最大流最小割定理

  • 可行流 $f$ 为最大流
  • 对于可行流 $f$,其 $G_{f}$ 不存在增广路。
  • 存在割 $[S,T]$,$|f|=c(S,T)$。

以上三条可以互相推出。

最大流等于最小割。

Part. 2 算法

Part. 1-1 Edmond-Karp

不会

Part. 1-2 Dinic

先讲讲暴力 FF。

算了懒得讲了自己看洛谷题解的暴力代码吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10010, E = 200010;

int n, m, s, t;
LL first[N];
LL to[E], nxt[E], val[E]/*残余容量*/;
int cnt = 1;
//cnt初值1,第一条边的标号为2(二进制10),第二条是3(二进制11) 
//有啥好处呢?
//我们加入一条边时,紧接着加入它的反向边(初始容量0) 
//这两条边的标号就是二进制最后一位不相同,一个0、一个1
//所以要召唤 p 这条边的反向边,只需用 p ^ 1
//如果cnt初值为0,就做不到。当然初值-1也可以,略需改动

//关于图中真正的反向边,可能引起顾虑,应该让它们标号相邻? 
//其实不用。该找到的增广路都会找到的 

bool vis[N];//限制增广路不要重复走点,否则很容易爆栈 
//兜一大圈走到汇点,还不如直接走到汇点

void addE(int u, int v, LL w) {
    ++cnt;
    to[cnt] = v;
    val[cnt] = w;
    nxt[cnt] = first[u];
    first[u] = cnt;
}
LL dfs(int u, LL flow) {
    //注意,在走到汇点之前,无法得知这次的流量到底有多少 
    if (u == t)
        return flow;//走到汇点才return一个实实在在的流量 
    
    vis[u] = true;
    for (int p = first[u]; p; p = nxt[p]) {
        int v = to[p];
        if (val[p] == 0 or vis[v])//无残量,走了也没用 
            continue;
        int res = 0;
        if ((res = dfs(v, min(flow, val[p]))) > 0) {
            //↑顺着流过去,要受一路上最小容量的限制
            val[p] -= res;//此边残余容量减小
            val[p ^ 1] += res;//以后可以顺着反向边收回这些容量,前提是对方有人了 
            return res;
        }
    }
    return 0;//我与终点根本不连通(依照残量网络),上一个点不要信任我
}
int main() {
    scanf("%d %d %d %d", &n, &m, &s, &t);
    for (int i = 1; i <= m; ++i) {
        int u, v; LL w;
        scanf("%d %d %lld", &u, &v, &w);
        addE(u, v, w);
        addE(v, u, 0);//和正向边标号相邻
        //反向边开始容量为0,表示不允许平白无故走反向边
        //只有正向边流量过来以后,才提供返还流量的机会
    }
    LL res = 0, tot = 0;
    while (memset(vis, 0, sizeof(vis)) and (res = dfs(s, 1e18/*水库无限*/)) > 0)
        tot += res;//进行若干回合的增广

    printf("%lld\n", tot);
    return 0;
}

接下来讲讲如何 Dinic。

其实我不理解 Dinic 比暴力优在哪里

/* okay | there's been */

#include <cstdio>

namespace mySpace {
typedef long long LL;

const int MAXN = 200 + 5, MAXM = 5000 + 5;

int rint () {
    int x = 0, f = 1; char c = getchar ();
    for ( ; c < '0' || c > '9'; c = getchar () )    f = c == '-' ? -1 : f;
    for ( ; c >= '0' && c <= '9'; c = getchar () )    x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
    return x * f;
}

template<typename _T> _T MIN ( const _T x, const _T y ) { return x < y ? x : y; }

struct GraphSet {
    int to, nx;
    LL wt;
    GraphSet () : to ( 0 ), nx ( 0 ), wt ( 0 ) {}
    GraphSet ( const int a, const int b, const LL c ) : to ( a ), nx ( b ), wt ( c ) {}
} as[MAXM * 2];

int n, m, s, t, bgn[MAXN], cnt = 1, lav[MAXN], ali[MAXN];

void makeEdge ( const int u, const int v, const LL w ) { as[++ cnt] = GraphSet ( v, bgn[u], w ), bgn[u] = cnt; }

bool bfs () {
    for ( int i = 1; i <= n; ++ i )    lav[i] = 0;
    int nowl = 1, nowr = 1;
    ali[1] = s, lav[s] = 1;
    for ( ; nowl <= nowr; ) {
        int u = ali[nowl ++];
        for ( int i = bgn[u]; i; i = as[i].nx ) {
            int v = as[i].to; LL w = as[i].wt;
            if ( ! w || lav[v] )    continue;
            lav[v] = lav[u] + 1, ali[++ nowr] = v;
        }
    }
    return lav[t];
}

LL dfs ( const int u, LL in ) {
    if ( u == t )    return in;
    LL out = 0;
    for ( int i = bgn[u]; i; i = as[i].nx ) {
        if ( ! in )    break;
        int v = as[i].to; LL w = as[i].wt;
        if ( ! w || lav[v] != lav[u] + 1 )    continue;
        LL ret = dfs ( v, MIN ( in, w ) );
        as[i].wt -= ret, as[i ^ 1].wt += ret;
        in -= ret, out += ret;
    }
    if ( ! out )    lav[u] = 0;
    return out;
}

LL calcMXflow () {
    LL res = 0;
    for ( ; bfs (); res += dfs ( s, 1e18 ) ) ;
    return res;
}

void main () {
    n = rint (), m = rint (), s = rint (), t = rint ();
    for ( int i = 1; i <= m; ++ i ) {
        int u = rint (), v = rint (); LL w = rint ();
        makeEdge ( u, v, w ), makeEdge ( v, u, 0 );
    }
    printf ( "%lld\n", calcMXflow () );
}
}

int main () {
    mySpace :: main ();
    return 0;
}

Part. 3 无源汇上下界可行流

Part. 3-1 概念

就是每条边的容量从变成了 $[cl(u,v),cr(u,v)]$。

Part. 3-2 xxxx

可以想见通过把容量转化为 $[0,cr(u,v)-cl(uv)]$,但这样容量不一定能守恒。

记每个点的入流量为 $in_{u}$,出流为 $out_{u}$。

  • $in_{u}\ge out_{u}$

从 $S$ 向 $u$ 连一条容量 $in_{u}-out_{u}$ 的边。

  • $in_{u}<out_{u}$

从 $u$ 向 $T$ 连一条容量 $out_{u}-in_{u}$ 的边。

Part. 4 有源汇上下界最大流

Part. 4-1 概念

有规定的源点汇点了。

Part. 4-2 xxxx

图都是从 这里 偷的。这个博客几百年没过图了

20190824120232582.png

有源汇后,$s$ 和 $t$ 不满足流量守恒了,于是从 $s$ 到 $t$ 连一条容量 $[0,\infty]$ 的边即可。

20190824120304124.png

然后就可以把 $s$ 和 $t$ 当成普通点处理,重现建立超源超汇,用无源汇上下界可行流的方法跑一遍,看是不是一个可行流,如果是就从 $s$ 到 $t$ 跑一遍最大流。

Part. 5 有源汇上下界最小流

Part. 5-1 概念

你猜。

Part. 5-2 xxxx

首先加上 $(t,s)$ 的边跑最大流。

设 $d$ 是加上的边的流量,删掉加上的边。

答案是 $d-\text{maxflow}(t,s)$。

这里 copy 的。

Part. 6 多源汇最大流

Part. 6-1 概念

你猜。

Part. 6-2 xxxx

没啥区别,建超源超汇即可。

Prob. 1

Desc. & Link.

有一个基础想法,即一次操作三可以用一次操作一加上一次操作二来实现,然后他又没让我们最小化操作次数,所以我们令 $M=\min\{A+R,M\}$。

操作的顺序并不影响,所以为了方便我们可以将原数组排个序。

感觉花费是一个单峰。

三分吧。

过了。

草。

Prob. 2

Desc. & Link.

能递推吧?

考虑加入一个点对局势造成的影响。

首先我们只考虑红色蓝色的情况(剩下的放绿边就行了)。

这个图是完全图来着?

转化成 $n\times n$ 棋盘放棋子问题,每格最多放一个,同行同列不能同色(红蓝两 saier)。

然后不会了。

Prob. 3

Desc. & Link.

先行离散化,把 $a^{\text{sorted}}$ 记为 $b$。

由于 $b$ 是 $a$ 的一种排列,不是特别容易想到,但我们可以连个边(记住这种处理方式)。

我们对 $(b_{i},a_{i})$ 连边(有向边)。

然后就得到了一个每个节点自己的出入度数相同的图。

草又不会了。

Prob. 4

mmp 这题再不会人就没了。

Subtask one

看见括号先 $-1+1$,起始时每一个括号先假设成绿色。

先从左往右扫,如果此时右括号的数量大于了左括号的数量,我们就把序列中最前面的两个右括号分别染成红、蓝色,不足则无解。

后面懒得写了。

Subtask two

考虑 DP。

数据范围感觉区间 DP。

对不起我没读题。

重新考虑。

不会。

算了看题解。

贴个链接吧:Here