暑训记录7.15

昨晚cf打到12点半,被yy哥哥带飞,又能上分了嘿嘿

早上

昨天晚上补集训队oj上面的字典树题,wa到想死,今天早上想补网站还挂了。 。。

cf710 Div. 3 E. Restoring the Permutation

题目大意

给出一个长度为n的数组,ai表示前i个值里面的最大值,要求将数组还原成长度为n并且符合数组限制的排列,输出字典序最小的排列和最大的排列

题目思路

我们很容易就能发现当a[i]>a[i-1]时,i位置放的一定是a[i]。
根据这个结论我们可以确定一部分位置的值,这些位置的值也一定是单增的。
如果a[i]和a[j]都是能够确定的,并且i<j,i到j中间没有能够确定的值,那么i到j中所有值都应该是小于a[i]的。
对于字典序最小的情况,我们把未放置的数字从小到大放里面放,一定能够满足字典序最小和上面的要求。
对于字典序最大的情况,我们为了满足上述条件,需要每次经过一个确定的值a[i]时,把未放置并且小于a[i]的放进数组。
我们可以考虑用优先队列加双指针来模拟实现。
我们先假设i指针遍历数组查询第i个位置是否放了数,j指针遍历标记数组查询j是否放进了数组。
j指针初始化为1
首先我们先遍历1到n,如果当前位置放了a[x],那么我们移动j指针向后遍历到a[x],将未放置的数放进优先队列。当当前位置为空时,我们优先放置队列首部。这样操作下来一定是能够满足字典序最大的,并且复杂度为o(n+n)而不是纯暴力的o(n*n)

csustoj P4001 你真的会加法吗?

https://blog.csdn.net/daydreamer23333/article/details/118755890

这题是去年暑训的第一场选拔赛的题。但是字典树学完一知半解,看到这题字典树的想法都不知道。后来学长说是字典树,就想着等我重新学的时候去重写下。昨天晚上调了一晚上还是wa,重新看了学长的标称,研究了很久,还对拍搞了几组数据才过。这题一开始相当做数字做,然后很多地方不好处理,标程里面是直接当作字符串做。重写了一边发现确实好写多了。

下午

字典树就学到这把,开始重新学网络流了,去年学完又忘完了,中间训练的时候还碰到过几个网络流题目完全不会哈哈哈。

源点:网络流的起点,流量无限
汇点:网络流的终点
增广路:是从源点到汇点的一条路径,其上所有边的残余容量均大于0,流量等于增广路上所有边的流量最小值
反向边:流向相反的边,初值位0,正边减少值时,反向边增加相同大小的值,提供撤销功能
Ford-Fulkerson算法:用dfs找出存在的增广路,并维护答案
Edmond-Karp算法:用bfs找出存在的增广路,并维护答案
多路增广:即dfs找到一个增广路时向上回溯,当当前点还有边的容量不为0并且流量还有剩余就继续向下递归
当前弧优化:我们跑到一个节点时,dfs完他的一条边,遍历他的下一条边时,这条边的容量已经为空,我们之后都不能走这条边了。所以为了防止搜索时间浪费在这些容量为空的边上,我们每次跑到一条新边时,把链式前向行的head数组的对应起点的值更新为当前边
Dinic算法:上面两种算法的优化版,找增广路的方式是bfs和dfs结合。先用bfs对现存的图分层,类似树形结构里面求深度的方法(分层的好处是我们每次想下个点跑时只往深度小于自己的点跑,防止绕圈或者绕远路),然后dfs找增广路。如果分层时无法走到汇点说明没有增广路了,此时的答案就是最大流
模板:

int n, m, s, t, lv[MAXN], cur[MAXN]; // lv是每个点的层数,cur用于当前弧优化标记增广起点
inline bool bfs() // BFS分层
{
    memset(lv, -1, sizeof(lv));
    lv[s] = 0;
    memcpy(cur, head, sizeof(head)); // 当前弧优化初始化
    queue<int> q;
    q.push(s);
    while (!q.empty())
    {
        int p = q.front();
        q.pop();
        for (int eg = head[p]; eg; eg = edges[eg].next)
        {
            int to = edges[eg].to, vol = edges[eg].w;
            if (vol > 0 && lv[to] == -1)
                lv[to] = lv[p] + 1, q.push(to);
        }
    }
    return lv[t] != -1; // 如果汇点未访问过说明已经无法达到汇点,此时返回false
}
int dfs(int p = s, int flow = INF)
{
    if (p == t)
        return flow;
    int rmn = flow; // 剩余的流量
    for (int eg = cur[p]; eg && rmn; eg = edges[eg].next) // 如果已经没有剩余流量则退出
    {
        cur[p] = eg; // 当前弧优化,更新当前弧
        int to = edges[eg].to, vol = edges[eg].w;
        if (vol > 0 && lv[to] == lv[p] + 1) // 往层数高的方向增广
        {
            int c = dfs(to, min(vol, rmn)); // 尽可能多地传递流量
            rmn -= c; // 剩余流量减少
            edges[eg].w -= c; // 更新残余容量
            edges[eg ^ 1].w += c; // 再次提醒,链式前向星的cnt需要初始化为1(或-1)才能这样求反向边
        }
    }
    return flow - rmn; // 返回传递出去的流量的大小
}
inline int dinic()
{
    int ans = 0;
    while (bfs())
        ans += dfs();
    return ans;
}

:从网络中选择一些边,使得去掉这些边后,剩下两个不连通的分别包含源点和汇点的点集。割的大小是这些边的容量之和。在所有可行的割中,最小的割称为最小割。
最大流最小割定理:最大流等于最小割
证明博客链接:https://blog.csdn.net/jy02660221/article/details/83471968(证明看的大概能看懂,但是看完有感觉有点不明白)

洛谷P3376 【模板】网络最大流

代码

#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1h
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
using namespace std;
const int maxn = 2e6+10;
const int inf = 1e9;
const ll llinf =1e18+10;
const ll mod = 1e9+7;


int cnt=1;
struct node
{
    int to,next;
    ll w;
}e[maxn];
int first[maxn];
int n,m,s,t;
int dep[maxn];
int cur[maxn];

void add(int u,int v,ll w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=first[u];
    first[u]=cnt;
}

inline bool bfs()
{
    ms(dep,-1);
    dep[s]=0;
    memcpy(cur,first,sizeof(first));
    queue<int>q;
    q.push(s);

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i!=-1;i=e[i].next)
        {
            int to=e[i].to;
            ll vol=e[i].w;
            if(vol>0&&dep[to]==-1)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    return dep[t]!=-1;
}

inline ll dfs(int u=s,ll flow=llinf)
{

    if(u==t)
        return flow;
    ll last=flow;
    for(int i=cur[u];i!=-1&&last;i=e[i].next)
    {
        cur[u]=i;
        int to=e[i].to;
        ll vol=e[i].w;
        //if(last==0)break;
        //如果把last的判断条件从for里面拿到上面这里就会t,不动就过了,感觉很神奇
        if(vol>0&&dep[to]==dep[u]+1)
        {
            int c=dfs(to,min(vol,last));
            last-=c;
            e[i].w-=c;
            e[i^1].w+=c;

        }
    }
    return flow-last;
}
inline ll dinic()
{
    ll ans=0;
    while(bfs())
    {
        ans+=dfs();

    }
    return ans;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    ms(first,-1);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        ll w;
        scanf("%d%d%lld",&u,&v,&w);
        add(u,v,w);
        add(v,u,0);
    }

    printf("%lld\n",dinic());
}

晚上

看了下网络流好多各种各样的概念,有点头疼。
准备学完之后全部搬到一个博客里去

晚训三道题过了两道。
第一道简单签到题,
第二道简单贪心。
第三道题是线段树,看了很久还是不知道咋做,再次面向题解编程了。
感觉现在做线段树的题,脑子里面还是只有维护最大值、最小值、区间和,题目也不去分析,就一直硬想,有点自闭。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值