zoukankan      html  css  js  c++  java
  • D3 HL 模拟赛订正

    总结:我真sb,Chdy大佬拿走我饭卡没吃早饭,队列没清空导致T2炸锅,模型没掌握导致解题不畅,太菜导致被三位大佬踩;

    第一题:求无向图中1号点到n号点的必经点;

    其实拿到第一题,想写圆方树,后来证明这样的做法是正确的,也有大佬用支配树搞出来,但是支配树针对有向图;

    但是无向图让我去写了割点,让求x到y的必经割点,不就是嗅探器吗,题解点这里

    考虑如何求x,y两点之间的必经的割点。

    很明显需要满足4个条件

    考虑我们有一个u点,它连了一个v点,那么u点需要满足4个条件。

    1. u不是起点终点。因为题目要求在中间服务器上建嗅探器。
    2. u是割点。
    3. 终点(y)的dfn应该大于等于v点的dfn,因为要确保终点在v点或之后被访问到,即u点为必经的点。
    4. 终点(y)的low应该大于等于u点的dfn,因为要确保终点必须要经过u点。

    剩下的没什么了,板子

    注意我们从x点开始tarjan,就不用看x点是不是割点了。

    这个输出不忽略行末空格,贼恶心;

    #include<bits/stdc++.h>
    using namespace std;
    #define N 500001
    template<typename T>inline void read(T &x) {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int T,n,m,tot,x,y,lin[N],dfn[N],low[N],k,cnt,root,ans[N]; 
    struct gg
    {
        int y,next;
    }a[N<<1];
    void add(int x,int y)
    {
        a[++tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    void tarjan(int u)
    {
        dfn[u]=low[u]=++cnt;
        for(int i=lin[u];i;i=a[i].next)
        {
            int v=a[i].y;
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
                if(u!=x&&u!=y)
                    if(dfn[u]<=low[v]&&dfn[v]<=dfn[y]&&low[y]>=dfn[x])
                           ans[++k]=u;
            }
            else low[u]=min(low[u],dfn[v]);
        }
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        freopen("home.in","r",stdin);
        freopen("home.out","w",stdout);
        read(T);
        while(T--) {
            memset(lin,0,sizeof(lin));
            memset(dfn,0,sizeof(dfn));
            memset(lin,0,sizeof(lin));
            memset(ans,0,sizeof(ans));
            read(n); read(m);
            k=0,tot=0,cnt=0;
            for(int i=1;i<=m;i++) {
                read(x); read(y);
                if(x==y) continue;
                add(x,y); add(y,x);
            }
            x=1,y=n;
            tarjan(1);
            cout<<k<<endl;
            if(!k) {
                cout<<endl;
                continue;
            }
            sort(ans+1,ans+k+1);
            cout<<ans[1];
            for(int i=2;i<=k;i++) {
                cout<<' '<<ans[i];
            } 
            cout<<endl;
        }
        
        return 0;
    } 
    View Code

    第二题:植物大战僵尸

    好像是很多大佬做过的原题,作为一个网络流只学几天的蒟蒻还是有些东西没有想到的;

    如果你要攻击植物 i,那么你就必须把 i 的右边一棵植物、以及保护着它的所有植物都一起攻击掉 。

    这与最大权闭合子图中 对于每个点,从它出发,能够走到的所有点都属于闭合子图中 刚好契合。

    我们可以这样建图:

    植物 i 的点权设为 val[i],所有植物 i 向它的右边连边,如果一个植物 j 保护着 i , i 向 j 连边;

    在这个图上求出最大权闭合子图,答案就是最大收益,也就是问题的答案。

    但是,我们漏了一种情况,如果两棵植物互相保护(环),那么僵尸无论如何都无法攻击到它们。

    所以我们需要拓扑找一下环;

    这里感谢Chdy大佬改掉我‘TLE的代码,如果存在一个环后面连接一个环,那么我么直接将其忽略,而我原来的代码并没有这样;

    同理,被环所保护的节点也无法被攻击到。

    所有出现在环中以及被环保护的节点都应该被去除。我们可以先建一个反向图(这里是指相对于网络图的反向图),并进行拓扑排序,能够被拓扑排序遍历到的节点才能用来建图。

    #include<bits/stdc++.h>
    using namespace std;
    #define inf 1e9
    #define maxn 1000100
    template<typename T>inline void read(T &x) {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int n,m,s,t,tot=1;
    int d[5000],lin[maxn],du[5000],vis[5000],sc[5000];
    vector<int> g[5000];
    
    struct gg {
        int y,next,flow;
    }a[maxn<<1];
    
    inline void add(int x,int y,int flow) {
        a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].flow=flow;
        a[++tot].y=x; a[tot].next=lin[y]; lin[y]=tot; a[tot].flow=0;
    }
    
    inline bool bfs() {
        memset(d,0,sizeof(d));
        queue<int>q;
        q.push(s); d[s]=1;
        while (!q.empty()) {
            int x=q.front(); q.pop();
            for (int i=lin[x];i;i=a[i].next) {
                int y=a[i].y;
                if (a[i].flow&&!d[y]) {
                    d[y]=d[x]+1;
                    if(y==t) return 1;
                    q.push(y);
                }
            }
        }
        return 0;
    }
    
    inline int dinic(int x,int flow) {
        if (x==t) return flow;
        int rest=flow,k;
        for (int i=lin[x];i;i=a[i].next) {
            int y=a[i].y;
            if (a[i].flow&&d[y]==d[x]+1) {
                k=dinic(y,min(rest,a[i].flow));
                if (!k) d[y]=0;
                a[i].flow-=k;
                a[i^1].flow+=k;
                rest-=k;
            }
        }
        return flow-rest;
    }
    
    inline int num(int i,int j) {
        return (i-1)*m+j;
    }
    
    queue<int>Q;
    inline void topsort() {
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                if(!du[num(i,j)]) Q.push(num(i,j)),vis[num(i,j)]=1;
        
        while(!Q.empty()) {
            int x=Q.front();
            Q.pop();
        
            for (int i=0;i<g[x].size();++i) {
                int y=g[x][i];
                --du[y];
                add(y,x,inf); 
                if (!vis[y]&&!du[y]) Q.push(y),vis[y]=1;
            }
        }
    }
    
    int main() {
        freopen("B.in","r",stdin);
        freopen("B.out","w",stdout);
        
        read(n);read(m);
        
        for(int i=1;i<=n;++i)
            for(int j=1,w;j<=m;++j) {
                read(sc[num(i,j)]),read(w);
                for(int k=1,x,y;k<=w;++k) {
                    read(x);read(y);
                    ++x,++y;
                    g[num(i,j)].push_back(num(x,y));
                    ++du[num(x,y)];
                }
                if(j<m) {
                    g[num(i,j+1)].push_back(num(i,j));
                    ++du[num(i,j)];
                }
            }
        s=0,t=1010;
        int sum=0;    
        topsort();
        
        
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j) {
                int x=num(i,j);
                if(!vis[x]) continue;
                if(sc[x]>=0) add(s,x,sc[x]),sum+=sc[x];
                else add(x,t,-sc[x]);
            }
        
        int max_flow=0,flow=0;
        while(bfs())    max_flow+=dinic(s,inf);
        printf("%d
    ",sum-max_flow);
        return 0;
    }
    View Code

    第三题:

    题目描述

    给定一个长度n的整数序列hi

    需要通过一些操作修改序列成为单调不降序列,即hihi+1

    给定m种操作,

    第i种操作为付出ci的代价将一段长为li的连续子序列+1或-1(输入会给出)

    不限制每种操作的使用次数,

    hi可以被修改为任意整数,求最小代价,无解输出-1

    输入格式

    第一行两个整数n,m

    第二行n个整数hi

    接下来m行,每行的格式为opti,li,ci,其中opti是一个字符表示+1还是-1

    输出格式

    输出一行一个整数代表最小代价,若无解输出-1;

    题解:

    知道考网络流才往这个模型上想,很显然是个费用流,感觉石神说的对,网络流就考个建图;

    首先需要单调不下降,所以我们对原序列进行差分,一定都是非负数;

    所以考虑这样建图;

    将差分后的序列,进行判断,如果>0,我们将源点和它相连,容量为差分后序列对应的大小,花费为0;我们期望

    如果小于零我们将其连向汇点,代价为对应大小的绝对值,花费为0,我们还要记录一下负数绝对值的累加和sum,因为我们期望的是满流;

    对于一个操作,我们只需要暴力连上他所能连接的边(在这个区间)就可以了,具体方向的话,我们思考之后发现;

    如果是-1的操作,我们将j连向j+L,容量为inf,费用为c,因为我们可以使用无数次;

    如果是+1的操作,我们从j+L连向j,容量为inf,费用为c,都别忘了处理反向边;

    跑费用流即可,判断无解就是看最大流是否等于sum即可;

    #include<bits/stdc++.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    #define N 1000010 
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int tot=1,n,m,s,t,sum,l,c;
    int pre[N],lin[N],vis[N],dis[N],incf[N],h[N],g[N];
    long long max_flow,ans;
    struct gg {
        int y,next,flow,c;
    }a[N<<1];
    
    inline void add(int x,int y,int flow,int c) {
        a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].c=c; a[tot].flow=flow;
        a[++tot].y=x; a[tot].next=lin[y]; lin[y]=tot; a[tot].c=-c; a[tot].flow=0;
    }
    
    inline bool spfa()
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        queue<int>q;
        q.push(s);
        dis[s]=0,vis[s]=1,incf[s]=1<<30;
        while(!q.empty()) {
            int x=q.front();
            q.pop();
            vis[x]=0;
            for(int i=lin[x];i;i=a[i].next) {
                if(!a[i].flow) continue;
                int y=a[i].y;
                if(dis[y]>dis[x]+a[i].c) {
                    dis[y]=dis[x]+a[i].c;
                    incf[y]=min(incf[x],a[i].flow);
                    pre[y]=i;
                    if(!vis[y]) q.push(y),vis[y]=1;
                }
            }
        }
        if(dis[t]==inf) return false;
        else return true;
    }
    
    inline void update() {
        int x=t;
        while(x!=s) {
            int i=pre[x];
            a[i].flow-=incf[t];
            a[i^1].flow+=incf[t];
            x=a[i^1].y;
        }
        max_flow+=incf[t];
        ans+=dis[t]*incf[t];
    }
    
    int main(){
        //freopen("1.in","r",stdin);
        freopen("seq.in","r",stdin);
        freopen("seq.out","w",stdout);
        read(n); read(m);
        s=n+2,t=n+3; 
        for(int i=1;i<=n;i++)
            read(h[i]);
        for(int i=1;i<=n;i++) 
            g[i]=h[i]-h[i-1];//搞一个差分序列; 
        g[1]=g[n+1]=inf;//特殊处理1和n的情况 
        for(int i=1;i<=n+1;i++)
            if(g[i]>0) add(s,i,g[i],0);
            else if(g[i]<0) add(i,t,-g[i],0),sum-=g[i];
        for(int i=1;i<=m;i++) {
            char opt[2];scanf("%s",opt);
            read(l);read(c);
            for(int j=1; j+l<=n+1; ++j)
                if(opt[0]=='-') add(j,j+l,inf,c);
                else add(j+l,j,inf,c);
        }
        while(spfa()) update();
        if(max_flow!=sum) ans=-1;
        cout<<ans<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    牛客网 剑指Offer JZ19 顺时针打印矩阵
    牛客网 剑指Offer Z18 二叉树的镜像
    牛客网 剑指Offer JZ17 树的子结构
    一键安装脚本(MySQL双主、EMQX集群、FastDFS集群)
    API和拦截器相关的设计简记
    C# 获取今天,昨天,上周,下周,上月,下月等等一些日期格式
    Element.scrollIntoView()
    自主学习(1)--matlab
    四则运算java
    学习计划
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11153855.html
Copyright © 2011-2022 走看看