首页 > 其他分享 >9月做题纪要

9月做题纪要

时间:2024-09-05 21:35:59浏览次数:10  
标签:Head ver int tot Edge 做题 Dinic 纪要

9.3/9.4

P3376 【模板】网络最大流

因为 Dinic 对于求最大流是比较优的算法,考虑对 Dinic 进行一个复习

Dinic 属于 Ford-Fulkerson 增广路算法,每次增广前我们都先用 BFS 将图分层,每个点的层数都是其距离源点的最短距离

求解思路如下:

  • 对原图进行 BFS 构建分层图

  • 考虑 EK 算法的核心问题在于一次只能扩展一条增广路,我们在 Dinic 中为了优化考虑使用 DFS 来一次寻找多条增广路

    DFS 可以同时寻找出多条增广路,因此相对来说 Dinic 对于 EK 算法较优

此时我们就完成了 Dinic 算法的基础,乍一看单次 DFS 时间复杂度是 \(O(VE)\) 的,但是我们发现一个问题:DFS 可能会经过很多次同一个点,那么复杂度就是不严格的

考虑对 Dinic 进行优化

  • 当前弧优化:

    这个优化力度较大,而且直接作用于复杂度上,不会负优化,负优化就是打假了

    由于我不知道怎么描述所以直接用 OI-wiki 上内容

    如果某一时刻我们已经知道边 \((u, v)\) 已经增广到极限(边 \((u, v)\) 已无剩余容量或 \(v\) 的后侧已增广至阻塞),则 \(u\) 的流量没有必要再尝试流向出边 \((u, v)\)。

    因此对于每个结点 \(u\),我们直接维护 \(u\) 的出边表中第一条还有必要尝试的出边,下次就直接从这条边开始推,因为这个边之前的肯定已经推满了

    我们称维护的这个指针为当前弧,那么这个优化就是当前弧优化

    在使用当前弧优化的情况下单次 DFS 复杂度是严格的 \(O(VE)\)

    • 证明

      每次找增广路最多找 \(|E|\) 条,长度最多为 \(|V|\),分层图层数严格递增,最多会建 \(|V|\) 次分层图

      那么 Dinic 的复杂度就是 \(O(V^2E)\) 的

  • 无用点优化

    如果有流量流向一个点的时候,这个点流不动了,那么就说明它在当前分层图上不再能做出贡献,可暂时删去

点击查看代码
int Head[N],ver[N],Edge[N],nxt[N],d[N];
int now[N],tot,n,m,s,t,maxflow;

inline void Add(int x,int y,int z){
    ver[++tot] = y;
    Edge[tot] = z;
    nxt[tot] = Head[x];
    Head[x] = tot; 

    ver[++tot] = x;
    Edge[tot] = 0;
    nxt[tot] = Head[y];
    Head[y] = tot; 
}

queue<int> q;
inline bool BFS(){
    memset(d,0,sizeof(d));
    while(!q.empty()) q.pop();
    q.push(s);
    d[s] = 1; now[s] = Head[s];
    while(q.size()){
        int x = q.front(); q.pop();
        for(int i=Head[x] ; i ; i=nxt[i]){
            if(Edge[i] && !d[ver[i]]){
                q.push(ver[i]);
                now[ver[i]] = Head[ver[i]];
                d[ver[i]] = d[x] + 1;
                if(ver[i] == t) return 1;
            }
        }
    }
    return 0;
}

inline int Dinic(int x,int f){
    if(x==t) return f;
    int rest = f;
    for(int i = now[x];i && rest;i = nxt[i]){
        now [x] = i;
        if(Edge[i] && d[ver[i]] == d[x] + 1){
            int k = Dinic(ver[i],min(rest,Edge[i]));
            if(!k) d[ver[i]] = 0;
            Edge[i] -= k;
            Edge[i^1] += k;
            rest -= k;
        }
    }
    return f - rest;
}

inline void In(){
    cin >> n >> m >> s >> t;
    tot = 1;
    for_(i,1,m){
        int x,y,z;
        cin >> x >> y >> z;
        Add(x,y,z);
    }
    while(BFS())
        maxflow += Dinic(s,inf);
        
    cout << maxflow << endl;
}

9.5

P4722 【模板】最大流 加强版 / 预流推进

我们发现这道题和上一道题基本没有区别,主要的区别是本题数据范围开的更大了,\(O(V^2E)\) 的 Dinic 无法通过

经过测试,朴素 Dinic 在本题只有 \(16\) 分,考虑对 Dinic 进行优化(因为我不会预流推进)

对于 Dinic 有三种做法:Improved DinicLCT DinicDinic 的玄学优化

个人采用的是 Dinic 的玄学优化

  • 先不算反向边跑一边 Dinic,计算反向边的权值,然后加上权值之后再跑一边 Dinic

  • 将所有边按照其容量二进制下的位数从大到小排序,位数相同的合并成一段。

    顺序枚举每一段,在残量网络中加入这一段所有的边,跑一次Dinic,将新增广的流量加入答案

这样就可以通过本题


(模拟赛)

OI赛制怎么不给大样例啊

T1 dance

最开始想了个假结论然后被自己 \(hack\) 了,\(hack\) 数据010010001

乱搞了一个神秘做法不知道会不会被卡,考虑先记录每个男生/女生所处的位置,然后从大到小枚举区间,看区间内男生和女生人数是否相同,若相同则直接跳出,否则继续寻找

我们先判断男生和女生哪一个更多,然后在记录时记录其中较少的那个,最劣复杂度 \(O(tot^2)\),\(tot\) 为男生和女生中较少的一个

然后特判一些情况,剩下的听天由命,然后果然有问题

int n,pos[N],tot,Sum;
bool a[N];
inline void In(){
    cin >> n;
    for_(i,1,n){
        cin >> a[i];
        if(a[i]) Sum+=1;
    }
    if(Sum==0 || Sum==n) {
        cout << 0;
        return;
    }
    if(Sum==n-Sum){
        cout << n;
        return;
    }
    if(Sum>n-Sum){
        for_(i,1,n){
            if(!a[i]) pos[++tot] = i;
        }
    }
    else{
        for_(i,1,n){
            if(a[i]) pos[++tot] = i;
        }

    }
    _for(i,tot,1){
        for_(j,1,i-1){
            int sum = pos[i]-pos[j]+1; // 区间内人数总和
            int sum1 = i-j+1 ;// 区间男生/女生人数总和
            int sum2 = sum - sum1;// 区间女生/男生人数总和
            if(sum1==sum2){
                cout << sum1 + sum2; 
                return;
            }
        }
    }
    cout << 2;
    return;
}

考虑我们对这个求男生和女生人数的差值的前缀和,假设这个值是 \(t\),然后我们只需要在两侧找到相同的 \(t\) 即可

然后直接求出区间的长度,在这些长度里求最大值即可

复杂度 \(O(n)\)

int n,a[N],q[N],m=1000000,ans=-inf;
inline void In(){
	cin >> n;
	for_(i,1,n){
		cin >> a[i];
		if(!a[i])
            a[i] = -1;
		a[i] += a[i-1];
		if(!q[a[i]+m])
            q[a[i]+m] = i;
	}
	for_(i,1,n){
		if(!a[i])
            ans = max(ans,i);
	    else 
            ans = max(ans,i-q[a[i]+m]);
	}
	cout << ans;
}

T2 string

没看懂,没写,感觉打个 dfs 能拿分,正解一眼是 dp

T3 bignumber

这题是原,我做过

直接线段树+二分即可

int tot[N],A[N],m,n,q;
inline void add(int a,int val,int q,int l,int r){
    if(l==r){
        tot[q]=val;
        return;
    }
    if(mid(l,r)>=a) 
        add(a,val,q*2,l,mid(l,r));
    if(mid(l,r)<a) 
        add(a,val,q*2+1,mid(l,r)+1,r);
    tot[q]=max(tot[q*2],tot[q*2+1]);
}
inline int ask(int lc,int rc,int q,int l,int r){
    if(lc<=l&&r<=rc) 
        return tot[q];
    int maxa=-inf,maxb=-inf;
    if(lc<=mid(l,r))
        maxa=ask(lc,rc,q*2,l,mid(l,r));
    if(mid(l,r)<rc) 
        maxb=ask(lc,rc,q*2+1,mid(l,r)+1,r);
    return max(maxa,maxb);
}
inline void In(){
    int a;
    cin >> a >> m;
    for_(i,1,a){
        char ans;int qwq;
      	cin >> ans >> qwq;
        if(ans=='A'){
            add(n+1,(qwq+q)%m,1,1,a);
            n++;
        }
        else{
            cout << (q=ask(n-qwq+1,n,1,1,a)) << endl;
        }
    }
}

标签:Head,ver,int,tot,Edge,做题,Dinic,纪要
From: https://www.cnblogs.com/Vsinger-LuoTianYi/p/18399271

相关文章

  • 概期DP做题记录
    概期DPP3600考虑\(ans\in[1,x]\),那么有:\[\begin{aligned}E(ans)&=\sum_{i\in[1,x]}iP(ans=i)\\&=\sum_{i\in[1,x]}P(ans\geqi)\\&=\sum1-P(ans<i)\\&=x-\sumP(ans<i)\end{aligned}\]我们就只需要计......
  • 2024.9 做题记录
    9.1arc108e当已经选了\(a_l,a_r\)时,\((l,r)\)与外面无关。区间dp,\(dp_{l,r}=\frac{\sum_{k=l,a_l<a_k<a_r}^rdp_{l,k}+dp_{k,r}}{num_{l,r}}+1\)。维护\(num_{l,r},\sumdp_{l,k},\sumdp_{k,r}\)转移。9.2P5188模拟赛T2。容斥强行不选\(s\)这些材料,矩阵快速幂。......
  • ARC183D 做题记录
    超棒的贪心构造题。可以观察到每次操作的两个叶子,充要条件是路径上匹配边和非匹配边交替出现,操作完后全部取反。首先考虑答案上界,从是否能取到上界入手,是本题的突破口。考虑操作两个叶子\(x,y\),收益为\(dep[x]+dep[y]-2dep[\text{LCA}(x,y)]\)。若固定根\(r\),当\(\text......
  • CF 有趣题目做题笔记
    CF1157FMaximum_Balanced_CircleProblem题意:给出一个长度为\(n\)的序列\(a\),你可以选出序列的任意子集。记这个子集为\(b\),大小为\(k\),则需要满足\(\lvertb_i-b_{(i+1)\bmodk}\rvert\le1\)。你需要最大化\(k\)的值,并输出选出的子集\(b\)。Solution注意到最终......
  • codeforces做题记录(1924B)& 回顾线段树
    1924B.SpaceHarbour题意:n个点排成一行,其中某些点上面建有港湾,港湾有一个权值,对每个点我们定义点的权值为“左边(包括自己)第一个港湾上的权值\(\times\)到右边(包括自己)第一个港湾的距离”(保证在一开始1号和n号点上都有港湾)。有q次操作:操作1给定x和v,表示在x点上建立权值为v的......
  • lldxjw的做题记录
    01Balanced你需要构造一个长度为\(n\)、由\(01\)组成的字符串,同时需要满足\(m\)个条件。第\(i\)个条件由两个整数\(l_i,\r_i\)给出,表示字符串位于\([l_i,r_i]\)区间的字符必须是相同数量的\(0\)和\(1\)。请输出满足所有条件且字典序最小的字符串。可以证明在题......
  • 考研数学做题速度怎么提高
    前言目前大家都快结束强化的学习了,有的同学已经开始做套卷了,那么肯定会有很多同学感觉到时间不够用。因而提高做题速度就迫在眉睫。做题速度由于什么决定做题速度很大程度上是因为没有做题思路,从我们看到题到有完整的清晰的做题思路的时间的多少,决定了你做题的快慢。很多人......
  • 拉格朗日插值优化 DP 做题笔记
    本来想在洛谷题单里找斜率优化DP的,然后发现了一个拉格朗日插值优化DP的题单,就点进去尝试了一下。题单。于是先看了雨兔的题解,学了CF995F的做法,然后A了这个题。雨兔题解的链接和我的代码见CF上的提交记录。现在正在做后面的题。P3643[APIO2016]划艇\(a_i,b_i......
  • codeforces做题记录(1942D,1938J,1934D1,1933F)
    1942D.LearningtoPaint题目大意:给定一行白格子,可以将任意的格子染成黑色,最终形成多个黑色的连续段,对每个连续段[i,j]有一个权重(题目给定),为aij,每个染色方案的权值就是所有连续段的权值的和。要求所有染色方案中前k大的权值。注意事项:权重aij的范围是[-1e6,1e6],格子个数n<=10......
  • Ynoi 做题笔记(2024 年暑假)
    P9992[YnoiEasyRound2024]TEST_130之前大概想出来了,但是没想清楚。发现每次询问\(w,d\)就相当于算\(w\)子树里离\(w\)距离不超过\(d\)的点的贡献之和,\(w\)的贡献是\(d+1\)(因为\(N(w,0),N(w,1),\ldots,N(w,d)\)都可以),\(w\)往下第一层的每个点分别的贡......