• 图论知识补全


    黑科技向

    1.分层图最短路

    这种题呢,一般是有一个这样的模型:

    有一个分层图,在层中间连的边走需要$x$的花费,跨层需要$y$的花费,求$1$到$n$的最短路

    我们一般会用一个$dis[i][j]$来表示现在是在第$i$个点,第$j$层的最短路

    例题:bzoj2662

    有一个$n$个点$m$条边的无向图,现在你有$k$张符卡可以使一条边边权减半,求$1$到$n$的最短路

    这道题的“跨层转移”就是用的符卡的张数,每次多枚举一下当前用了几张符卡转移一下就可以了

    #include<iostream>
     #include<cstdio>
     #include<cstring>
     #include<algorithm>
     using namespace std;
     int n,m,k,cnt,ans;
     struct data{
         int next,to,dis;
     }edge[2010];
     int head[60],w[60][60];
     bool check[60][60];
     void add(int strat,int end,int dd){
         edge[++cnt].next=head[strat];
         edge[cnt].to=end;
         edge[cnt].dis=dd;
         head[strat]=cnt; 
     }
     void dijkstra(){
         memset(w,0x3f3f3f3f,sizeof(w));
         w[1][0]=0;
         int mn,tmp1,tmp2;
         while(1){
             mn=0x3f3f3f3f;
             for(int i=1;i<=n;i++)
                 for(int j=0;j<=k;j++)
                     if(!check[i][j]&&w[i][j]<mn){
                         mn=w[i][j];
                         tmp1=i;
                         tmp2=j;
                     }
             if(mn==0x3f3f3f3f)  break;
             check[tmp1][tmp2]=1;
             for(int i=head[tmp1];i;i=edge[i].next){
                 w[edge[i].to][tmp2]=min(w[edge[i].to][tmp2],w[tmp1][tmp2]+edge[i].dis);//!!!tmp2
                 w[edge[i].to][tmp2+1]=min(w[edge[i].to][tmp2+1],w[tmp1][tmp2]+edge[i].dis/2);   
             }
         } 
     }
     int main(){
         scanf("%d%d%d",&n,&m,&k);
         int u,v,d;
         for(int i=1;i<=m;i++){
             scanf("%d%d%d",&u,&v,&d);
             add(u,v,d);
             add(v,u,d);
         }
         dijkstra();
         ans=0x3f3f3f3f;
         for(int i=0;i<=k;i++) ans=min(ans,w[n][i]);
         printf("%d",ans);
         return 0;
     }
    View Code

    当然 还有一些要用到小trick的情况

    比如说 每层的图都一样 但是k很大卡你空间的时候

    就可以类似dp用滚动数组的方法

    每次跑当前层和下一层的最短路

    2.二分图的Hall定理

    wzj52501学长在BJWC上讲过...可是当时我在打炉石

    定理内容:

    设二分图中G=<V1,V2,E>中 |V1|=m<=|V2|=n,G中存在从V1到V2的完全匹配当且仅当V1中任意k(k=1,2,...,m)个顶点至少与V2中k个顶点是相邻的。

    这个可以把计算二分图存不存在完美匹配的复杂度降低

    举个例子

    bzoj1135

    初始时滑冰俱乐部有1到n号的溜冰鞋各k双。已知x号脚的人可以穿x到x+d的溜冰鞋。 有m次操作,每次包含两个数ri,xi代表来了xi个ri号脚的人。xi为负,则代表走了这么多人。 对于每次操作,输出溜冰鞋是否足够。

    朴素的想法是每来一个人做一次二分图匹配

    。。。但显然是超时的

    我们可以这样想

    根据Hall定理,左边需求任意k种鞋的人数肯定小于等于能满足他们需求的鞋的总数

    因为“任意”这个东西不好搞我们可以把它转换成“极值”

    就是当一些人需求的鞋的范围极小时人数依然小于等于需求的鞋数

    容易知道当人的集合是一段连续的区间时需求鞋的范围取到极小

    也就是

    $sum_{i = l}^{r}a_i leq (r - l + d + 1) imes k$

    $sum_{i = l}^{r}(a_i - k) leq d imes k$

    其中$a_i$表示需求鞋$i$的人数

    于是我们用线段树动态维护$(a_i - k)$的最大子段和,每次看是否小于$d imes k$就可以了

    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<vector>
    #include<map>
    #include<set>
    #include<queue>
    #include<string>
    #define inf 1000000000
    #define maxn 250000
    #define maxm 500+100
    #define eps 1e-10
    #define ll long long
    #define pa pair<int,int>
    #define for0(i,n) for(int i=0;i<=(n);i++)
    #define for1(i,n) for(int i=1;i<=(n);i++)
    #define for2(i,x,y) for(int i=(x);i<=(y);i++)
    #define for3(i,x,y) for(int i=(x);i>=(y);i--)
    #define mod 1000000007
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
        return x*f;
    }
    struct seg{ll l,r,lx,rx,mx,sum;}t[4*maxn];
    ll n,m,kk,d;
    inline void build(int k,int l,int r)
    {
        t[k].l=l;t[k].r=r;int mid=(l+r)>>1;
        t[k].lx=t[k].rx=t[k].mx=-kk;t[k].sum=-(r-l+1)*kk;
        if(l==r)return;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    }
    inline void pushup(int k)
    {
        int l=k<<1,r=k<<1|1;
        t[k].lx=max(t[l].lx,t[l].sum+t[r].lx);
        t[k].rx=max(t[r].rx,t[r].sum+t[l].rx);
        t[k].mx=max(t[l].rx+t[r].lx,max(t[l].mx,t[r].mx));
        t[k].sum=t[l].sum+t[r].sum;
    }
    inline void add(int k,int x,ll y)
    {
        int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
        if(l==r){t[k].sum+=y;t[k].mx=t[k].lx=t[k].rx=t[k].sum;return;}
        if(x<=mid)add(k<<1,x,y);else add(k<<1|1,x,y);
        pushup(k);
    }
    int main()
    {
        freopen("input.txt","r",stdin);
        freopen("output.txt","w",stdout);
        n=read();m=read();kk=read();d=read();
        build(1,1,n);
        while(m--)
        {
            int x=read();ll y=read();
            add(1,x,y);
            printf("%s
    ",t[1].mx<=d*kk?"TAK":"NIE");
        }
        return 0;
    }
    View Code
  • 相关阅读:

    队列
    Collection类
    Hashtable类、IdentityHashMap和WeakHashMap类
    LinkedHashMap类
    广播的种类:有序广播和无序广播
    自定义的BroadCastReceiver
    String的两个API,判断指定字符串是否包含另一字符串,在字符串中删除指定字符串。
    BroadcastReceiver的最简单用法
    Notification通知栏
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/9410491.html
Copyright © 2020-2023  润新知