开始学习,冲
BZOJ 狼抓兔子
懵逼一整天,终于搞明白平面图怎么转对偶图了
如果直接暴力跑最小割(最大流) 那么肯定TLE
根据平面图的性质 我们把它转化为对偶图跑最短路也就是原图的最小割
对偶图 洛谷博客
翻了一天了 对偶图的博客,终于找到了讲怎么转化的了
我们把每个面(小三角形) 看成点, 它与外界的公共边就是对偶图要阻挡的边,边权相同
#include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll int
//#define int long long
#define inf 0x3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
ll gcd(ll a,ll b){
if(a<0)a=-a;if(b<0)b=-b;return b==0?a:gcd(b,a%b);}
template<typename T>void read(T &res){
bool flag=false;char ch;while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
for(res=ch-48;isdigit(ch=getchar());res=(res<<1)+(res<<3)+ch - 48);flag&&(res=-res);}
ll lcm(ll a,ll b){
return a*b/gcd(a,b);}
ll qp(ll a,ll b,ll mod){
ll ans=1;if(b==0){
return ans%mod;}while(b){
if(b%2==1){
b--;ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans%mod;}//快速幂%
ll qpn(ll a,ll b, ll p){
ll ans = 1;a%=p;while(b){
if(b&1){
ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元 (分子*qp(分母,mod-2,mod))%mod;
ll fact_pow(ll n,ll p){
ll res=0;while(n){
n/=p;res+=n;}return res;}
ll mult(ll a,ll b,ll p)
{
a%=p;
b%=p;
ll r=0,v=a;
while(b)
{
if(b&1)
{
r=(r+v)%p;
r=(r+p)%p;
}
v<<=1;
v=(v+p)%p;
b>>=1;
}
return r%p;
}
ll pow_mod(ll x,ll n,ll mod)
{
ll res=1;
while(n)
{
if(n&1)
res=mult(res,x,mod);
x=mult(x,x,mod);
n>>=1;
}
return res;
}
ll quick_pow(ll a,ll b,ll p){
ll r=1,v=a%p;while(b){
if(b&1)r=mult(r,v,p);v=mult(v,v,p);b>>=1;}return r;}
bool CH(ll a,ll n,ll x,ll t)
{
ll r=quick_pow(a,x,n);ll z=r;for(ll i=1;i<=t;i++){
r=mult(r,r,n);if(r==1&&z!=1&&z!=n-1)return true;z=r;}return r!=1;}
bool Miller_Rabin(ll n)
{
if(n<2)return false;if(n==2)return true;if(!(n&1))return false;ll x=n-1,t=0;while(!(x&1)){
x>>=1;t++;}
srand(time(NULL));ll o=8;for(ll i=0;i<o;i++){
ll a=rand()%(n-1)+1;if(CH(a,n,x,t))return false;}return true;}
ll exgcd(ll a,ll b,ll &x,ll &y){
if (!b){
x=1,y=0;
return a;
}
ll ans=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return ans;
}
ll INV(ll a,ll b){
ll x,y;return exgcd(a,b,x,y),(x%b+b)%b;}
ll crt(ll x,ll p,ll mod){
return INV(p/mod,mod)*(p/mod)*x;}
ll FAC(ll x,ll a,ll b)
{
if(!x)return 1;ll ans=1;for(ll i=1;i<=b;i++)if(i%a)ans*=i,ans%=b;
ans=pow_mod(ans,x/b,b);for(ll i=1;i<=x%b;i++)if(i%a)ans*=i,ans%=b;return ans*FAC(x/a,a,b)%b;}
ll C(ll n,ll m,ll a,ll b)
{
ll N=FAC(n,a,b),M=FAC(m,a,b),Z=FAC(n-m,a,b),sum=0,i;for(i=n;i;i=i/a)sum+=i/a;
for(i=m;i;i=i/a)sum-=i/a;for(i=n-m;i;i=i/a)sum-=i/a;return N*pow_mod(a,sum,b)%b*INV(M,b)%b*INV(Z,b)%b;}
ll exlucas(ll n,ll m,ll p)
{
ll t=p,ans=0,i;for(i=2;i*i<=p;i++){
ll k=1;while(t%i==0){
k*=i,t/=i;}
ans+=crt(C(n,m,i,k),p,k),ans%=p;}if(t>1)ans+=crt(C(n,m,t,t),p,t),ans%=p;return ans % p;}
ll H(ll x,ll p) //错排
{
ll ans=0;
if(x==0)return 1;
x=x%(2*p);
if(x==0)x=2*p;
for(int i=2;i<=x;++i)
ans=(ans*i+(i%2==0?1:-1))%p;
return (ans+p)%p;
}
const int manx=2222222;
const int mamx=6666666;
ll head[manx],d[manx];
bool vis[manx];
ll n,m,k=0,s,e,kk,ans;
struct node{
ll v,next,w;
}a[mamx];
void add(ll u,ll v,ll w)
{
a[++k].next=head[u];
a[k].w=w;
a[k].v=v;
head[u]=k;
}
void dij()
{
memset(d,inf,sizeof(d));
memset(vis,0,sizeof(vis));
d[s]=0;
priority_queue<pair<ll,ll> >q;
q.push(mp(0,s));
while(q.size()){
ll u=q.top().se;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=a[i].next){
ll v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w){
//printf("qwq %lld\n",d[v]); //松弛操作,更新距离
d[v]=d[u]+w;
// printf("sad %lld %lld %lld %lld\n",d[u],u,res,d[v]);
q.push(mp(-d[v],v)); //把更新的距离和点入队,这里距离取负变成小根堆
}
}
}
}
ll pos[2100][1100];
signed main(){
ll zyx;
read(n);
read(m);
ll tot=0;
for(int i=1;i<=n*2-2;i++){
for(int j=1;j<m;j++){
//平面标号
pos[i][j]=++tot;
}
}
s=0;
zyx=tot+1;
//=++tot; //起点
//zyx=s+1;//终点
for(int i=1,id=1;i<=n;i++,id=id+2){
for(int j=1;j<=m-1;j++){
ll u=s;
ll v=zyx;
ll w;
read(w);
if(i!=1){
v=pos[id-1][j];
}
if(i!=n){
u=pos[id][j];
}
add(u,v,w);
add(v,u,w);
}
}
for(int i=1,id=1;i<=n-1;i++,id=id+2){
for(int j=1;j<=m;j++){
ll u=s;
ll v=zyx;
ll w;
read(w);
if(j!=1){
u=pos[id][j-1];
}
if(j!=m){
v=pos[id+1][j];
}
add(u,v,w);
add(v,u,w);
}
}
for(int i=1,id=1;i<=n-1;i++,id=id+2){
for(int j=1;j<=m-1;j++){
ll u=pos[id][j];
ll v=pos[id+1][j];
ll w;
read(w);
add(u,v,w);
add(v,u,w);
}
}
dij();
printf("%lld\n",d[zyx]);
return 0;
}
牛客暑假2020多校第二场I题 对偶图Interval
首先对偶图的应用特点是只能用于网格
题目的大概意思就是人在(1,n) 每次可以上下 左右走 现在要颁布m条禁令, 分为两种 L,R L禁令 那么对于禁令对应的位置,你是不可以移动到他的右边一个单位,也不可以从别的地方移动到禁令的位置 R禁令就是不能上下移动 ,现在问使得人不能到达L=R 的位置 最小的禁令花费是多少,如果不能阻挡,输出-1
显然如果人能够从(1,n)走到 (n,1) 一定是无解的
也就是说如果从起点到 (n,1) 是联通的则一定无解,最小的代价就是鸽边以后使得不联通的最小代价,那么我们只需要把禁令边鸽掉一些,正常边 边容建立inf (不可鸽) 跑一次Dinic 就OK 显然会TLE 那么就联想到平面图转化成对偶图跑最短路吧 对于L =R以下的区域其实我们可以不考虑
然后就是毒瘤的建立对偶图的过程
#include<bits/stdc++.h>
#include<stdlib.h>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll long long
#define int long long
#define inf 0x3f3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define IOS ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
ll gcd(ll a,ll b){
if(a<0)a=-a;if(b<0)b=-b;return b==0?a:gcd(b,a%b);}
template<typename T>void read(T &res){
bool flag=false;char ch;while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
for(res=ch-48;isdigit(ch=getchar());res=(res<<1)+(res<<3)+ch - 48);flag&&(res=-res);}
ll lcm(ll a,ll b){
return a*b/gcd(a,b);}
ll qp(ll a,ll b,ll mod){
ll ans=1;if(b==0){
return ans%mod;}while(b){
if(b%2==1){
b--;ans=ans*a%mod;}a=a*a%mod;b=b/2;}return ans%mod;}//快速幂%
ll qpn(ll a,ll b, ll p){
ll ans = 1;a%=p;while(b){
if(b&1){
ans = (ans*a)%p;--b;}a =(a*a)%p;b >>= 1;}return ans%p;}//逆元 (分子*qp(分母,mod-2,mod))%mod;
ll fact_pow(ll n,ll p){
ll res=0;while(n){
n/=p;res+=n;}return res;}
ll mult(ll a,ll b,ll p)
{
a%=p;
b%=p;
ll r=0,v=a;
while(b)
{
if(b&1)
{
r=(r+v)%p;
r=(r+p)%p;
}
v<<=1;
v=(v+p)%p;
b>>=1;
}
return r%p;
}
ll pow_mod(ll x,ll n,ll mod)
{
ll res=1;
while(n)
{
if(n&1)
res=mult(res,x,mod);
x=mult(x,x,mod);
n>>=1;
}
return res;
}
ll quick_pow(ll a,ll b,ll p){
ll r=1,v=a%p;while(b){
if(b&1)r=mult(r,v,p);v=mult(v,v,p);b>>=1;}return r;}
bool CH(ll a,ll n,ll x,ll t)
{
ll r=quick_pow(a,x,n);ll z=r;for(ll i=1;i<=t;i++){
r=mult(r,r,n);if(r==1&&z!=1&&z!=n-1)return true;z=r;}return r!=1;}
bool Miller_Rabin(ll n)
{
if(n<2)return false;if(n==2)return true;if(!(n&1))return false;ll x=n-1,t=0;while(!(x&1)){
x>>=1;t++;}
srand(time(NULL));ll o=8;for(ll i=0;i<o;i++){
ll a=rand()%(n-1)+1;if(CH(a,n,x,t))return false;}return true;}
ll exgcd(ll a,ll b,ll &x,ll &y){
if (!b){
x=1,y=0;
return a;
}
ll ans=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return ans;
}
ll INV(ll a,ll b){
ll x,y;return exgcd(a,b,x,y),(x%b+b)%b;}
ll crt(ll x,ll p,ll mod){
return INV(p/mod,mod)*(p/mod)*x;}
ll FAC(ll x,ll a,ll b)
{
if(!x)return 1;ll ans=1;for(ll i=1;i<=b;i++)if(i%a)ans*=i,ans%=b;
ans=pow_mod(ans,x/b,b);for(ll i=1;i<=x%b;i++)if(i%a)ans*=i,ans%=b;return ans*FAC(x/a,a,b)%b;}
ll C(ll n,ll m,ll a,ll b)
{
ll N=FAC(n,a,b),M=FAC(m,a,b),Z=FAC(n-m,a,b),sum=0,i;for(i=n;i;i=i/a)sum+=i/a;
for(i=m;i;i=i/a)sum-=i/a;for(i=n-m;i;i=i/a)sum-=i/a;return N*pow_mod(a,sum,b)%b*INV(M,b)%b*INV(Z,b)%b;}
ll exlucas(ll n,ll m,ll p)
{
ll t=p,ans=0,i;for(i=2;i*i<=p;i++){
ll k=1;while(t%i==0){
k*=i,t/=i;}
ans+=crt(C(n,m,i,k),p,k),ans%=p;}if(t>1)ans+=crt(C(n,m,t,t),p,t),ans%=p;return ans % p;}
ll H(ll x,ll p) //错排
{
ll ans=0;
if(x==0)return 1;
x=x%(2*p);
if(x==0)x=2*p;
for(int i=2;i<=x;++i)
ans=(ans*i+(i%2==0?1:-1))%p;
return (ans+p)%p;
}
const int manx=3*1e5+500;
const int mamx=2*1e6+700;
ll head[manx],d[manx];
bool vis[manx];
ll n,m,k=0,s,e,kk,ans;
struct node{
ll v,next,w;
}a[mamx];
void add(ll u,ll v,ll w)
{
a[++k].next=head[u];
a[k].w=w;
a[k].v=v;
head[u]=k;
}
void dij()
{
memset(d,inf,sizeof(d));
memset(vis,0,sizeof(vis));
d[s]=0;
priority_queue<pair<ll,ll> >q;
q.push(mp(0,s));
while(q.size()){
ll u=q.top().se;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=a[i].next){
ll v=a[i].v,w=a[i].w;
if(d[v]>d[u]+w){
//printf("qwq %lld\n",d[v]); //松弛操作,更新距离
d[v]=d[u]+w;
// printf("sad %lld %lld %lld %lld\n",d[u],u,res,d[v]);
q.push(mp(-d[v],v)); //把更新的距离和点入队,这里距离取负变成小根堆
}
}
}
}
ll pos[2100][1100];
signed main(){
ll zyx;
read(n);
read(m);// 警铃
ll tot=0;
for(int i=1;i<n;i++){
for(int j=i;j<n;j++){
//平面标号
pos[i][j]=++tot;
}
}
s=tot+1;
zyx=tot+2; //终点
for(int i=1;i<=m;i++){
ll u,v,w;
char op[5];
read(u);
read(v);
scanf("%s",op);
read(w);
if(op[0]=='L'){
if(v==n){
add(pos[u][v-1],zyx,w);
add(zyx,pos[u][v-1],w);
//链接汇点
}
else{
add(pos[u][v-1],pos[u][v],w);
add(pos[u][v],pos[u][v-1],w);
}
}
if(op[0]=='R'){
if(u==1){
add(s,pos[u][v-1],w);
add(pos[u][v-1],s,w);
//链接s点
}
else{
add(pos[u][v-1],pos[u-1][v-1],w);
add(pos[u-1][v-1],pos[u][v-1],w);
}
}
}
dij();
if(d[zyx]>=inf){
printf("-1\n");
return 0;
}
printf("%lld\n",d[zyx]);
return 0;
}
完美匹配
emmm 由于是个无向图所以要建立双向流量边
(我这个SB又去拆点了发现跑不出来) 直接连个起点 其他的建立双向就OK Dinic跑出来的maxflow除2 (有手就行,就不贴代码了)
https://paste.ubuntu.com/p/zs9vZrGQBF/
二分图的最小点覆盖
X坐标看作左集合 Y左边看着右集合 对1 建立匹配边
maxflow=二分图最小顶点覆盖
https://paste.ubuntu.com/p/C52G3hKrXB/
2-SAT入门题
写WA了 虽然也预感写错了,要开始学新算法了
网络流&&拆边
get新姿势 拆边建图 一开始我不知道怎么处理这个 cost=aix2 的花费
很愚蠢的改了改SPFA里面的东西 发现不对劲 ,后来想了想,拆成容量 w条边,每条边容量为1,花费递增,也就是说第一条边花费为1co 第二条边的意义就是 运输单位为2的货物 实际上是要花4cost的钱,但是我们拆了边,连同第一条边也就是说 现在第二条边只需要花费3cost的钱,累加,总花费还是不变的,就很妙的把这个计费转化了
#include <bits/stdc++.h>
#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include<queue>
#include <time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll int
//#define int long long
//#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x & (-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define Re register int
using namespace std;
const int N=500,M=2*1e6+3,inf=2e9;
int x,y,z,w,o=1,n,m,h,t,st,ed,cyf[N],pan[N],pre[N],dis[N],head[N];
ll mincost,maxflow;
struct QAQ{
int w,to,next,flow;}a[M<<1];
queue<int>Q;
inline void read(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void add(Re x,Re y,Re z,Re w){
a[++o].flow=z,a[o].w=w,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void add_(Re a,Re b,Re flow,Re w){
add(a,b,flow,w),add(b,a,0,-w);}
inline int SPFA(Re st,Re ed){
for(Re i=0;i<=n+1;++i)dis[i]=inf,pan[i]=0; //有多少个点就初始化到那个地方去
Q.push(st),pan[st]=1,dis[st]=0,cyf[st]=inf;
while(!Q.empty()){
Re x=Q.front();Q.pop();pan[x]=0;
for(Re i=head[x],to;i;i=a[i].next)
if(a[i].flow&&dis[to=a[i].to]>dis[x]+a[i].w){
dis[to]=dis[x]+a[i].w,pre[to]=i;
cyf[to]=min(cyf[x],a[i].flow);
if(!pan[to])pan[to]=1,Q.push(to);
}
}
return dis[ed]!=inf;
}
inline void EK(Re st,Re ed){
while(SPFA(st,ed)){
Re x=ed;maxflow+=cyf[ed],mincost+=(ll)(cyf[ed])*dis[ed];
while(x!=st){
//和最大流一样的更新
Re i=pre[x];
a[i].flow-=cyf[ed];
a[i^1].flow+=cyf[ed];
x=a[i^1].to;
}
}
}
void cle(){
o=1;
maxflow=0;
mincost=0;
memset(head,0,sizeof(head));
}
signed main(){
ll k;
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
// if(n<=0||m<=0||k<=0)
cle();
add_(0,1,k,0);
add_(n,n+1,k,0);
for(int i=1;i<=m;i++){
ll u,v,w,co;
read(u);
read(v);
read(co);// 花费
read(w); //容量
for(int j=1;j<=w;j++){
ll ne=(2*j-1)*co;
add_(u,v,1,ne);
}
}
st=0;
ed=n+1;
EK(st,ed);
if(maxflow<k){
printf("-1\n");continue;}
printf("%d\n",mincost);
}
return 0;
}
最小环覆盖&&费用流
一个无向图, 每一个点都必须属于一个圈, 并且只能属于一个圈, 求满足要求的最小费用 ; 显然一个点出度入度都为1 我们可以设置流量为1 价值为它的权值 用费用流来求解 最终如果maxflow不等于n 说明无解
无法构成圈 太坑了 有重边没判,一直TLE
#include <bits/stdc++.h>
#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include<queue>
#include <time.h>
#include <cstdio>
#include <iostream>
#include <vector>
#define ll int
//#define int long long
//#define inf 0x3f3f3f3f
#define mods 1000000007
#define modd 998244353
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x & (-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
#define Re register int
using namespace std;
const int N=1e5,M=2*1e6+3,inf=1e9+100;
int x,y,z,w,o=1,n,m,h,t,st,ed,cyf[N],pan[N],pre[N],dis[N],head[N];
ll mincost,maxflow;
struct QAQ{
int w,to,next,flow;}a[M<<1];
queue<int>Q;
inline void read(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void add(Re x,Re y,Re z,Re w){
a[++o].flow=z,a[o].w=w,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void add_(Re a,Re b,Re flow,Re w){
add(a,b,flow,w),add(b,a,0,-w);}
inline int SPFA(Re st,Re ed){
for(Re i=0;i<=2*n+1;++i)dis[i]=inf,pan[i]=0; //有多少个点就初始化到那个地方去
Q.push(st),pan[st]=1,dis[st]=0,cyf[st]=inf;
while(!Q.empty()){
Re x=Q.front();Q.pop();pan[x]=0;
for(Re i=head[x],to;i;i=a[i].next)
if(a[i].flow&&dis[to=a[i].to]>dis[x]+a[i].w){
dis[to]=dis[x]+a[i].w,pre[to]=i;
cyf[to]=min(cyf[x],a[i].flow);
if(!pan[to])pan[to]=1,Q.push(to);
}
}
return dis[ed]!=inf;
}
inline void EK(Re st,Re ed){
while(SPFA(st,ed)){
Re x=ed;maxflow+=cyf[ed],mincost+=(ll)cyf[ed]*dis[ed];
while(x!=st){
//和最大流一样的更新
Re i=pre[x];
a[i].flow-=cyf[ed];
a[i^1].flow+=cyf[ed];
x=a[i^1].to;
}
}
}
ll e[1100][1100];
void cle(){
o=1;
maxflow=0;
mincost=0;
memset(head,0,sizeof(head));
memset(e,inf,sizeof(e));
}
signed main(){
ll tt;
read(tt);
ll ca=0;
while(tt--){
read(n);
read(m);
ca++;
cle();
for(int i=1;i<=n;i++){
add_(0,i,1,0);
add_(i+n,2*n+1,1,0);
}
for(int i=1;i<=m;i++){
ll u,v,w;
read(u);
read(v);
read(w);
e[u][v]=min(e[u][v],w);
e[v][u]=e[u][v];
// add_(u,v+n,1,w);
// add_(v,u+n,1,w);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j){
continue;}
if(e[i][j]<inf){
add_(i,j+n,1,e[i][j]);
// add_(j,i+n,1,e[i][j]);
}
}
}
st=0;
ed=2*n+1;
EK(st,ed);
if(maxflow!=n){
printf("Case %d: NO\n",ca);
// printf("NO\n");
// continue;
}
else{
printf("Case %d: %d\n",ca,mincost);
}
}
return 0;
}