黑科技向
1.分层图最短路
这种题呢,一般是有一个这样的模型:
有一个分层图,在层中间连的边走需要$x$的花费,跨层需要$y$的花费,求$1$到$n$的最短路
我们一般会用一个$dis[i][j]$来表示现在是在第$i$个点,第$j$层的最短路
例题:bzoj2662
有一个$n$个点$m$条边的无向图,现在你有$k$张符卡可以使一条边边权减半,求$1$到$n$的最短路
这道题的“跨层转移”就是用的符卡的张数,每次多枚举一下当前用了几张符卡转移一下就可以了
#include#include #include #include 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=
这个可以把计算二分图存不存在完美匹配的复杂度降低
举个例子
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#include #include #include #include #include #include #include