图论知识补全
黑科技向
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; }
当然 还有一些要用到小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) \times k$
$\sum_{i = l}^{r}(a_i - k) \leq d \times k$
其中$a_i$表示需求鞋$i$的人数
于是我们用线段树动态维护$(a_i - k)$的最大子段和,每次看是否小于$d \times 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\n",t[1].mx<=d*kk?"TAK":"NIE"); } return 0; }