传送门:bzoj2965
题解
平面图转对偶图后跑个最小割即可。
p
≤
10
p\leq 10
p≤10,所以直接
2
p
2^{p}
2p枚举所有情况即可。
平面图转对偶图的具体过程:
首先将每条边拆成两条有向边,将每个点的出边按斜率排序。
这里假设有向边左侧的面属于内部。
枚举未访问的边,从当前边终点不断向外拓展:每次选择严格小于当前边反向边斜率的斜率最大的一条边(显然在为当前极小平面的边)走(若不存在严格小于当前边反向边斜率的边,则是最外的无限平面,走斜率最大的即可),直到走回初始边起点。
upd.听说这叫最小左转法?
最后需要注意拓展的同时记录下有向面积(为负即是无限平面)和在所有有向边左侧的古迹点(在当前极小面内)。
代码
#include<bits/stdc++.h>
#define pb push_back
#define vi vector<int>::iterator
#define ops(x) ((x&1)?(x+1):(x-1))
#define trs(x) ot[x]?num+2:x
typedef double db;
using namespace std;
const db eps=1e-8;
const int N=2e5+10,inf=0x7f7f7f7f;
int hty,num,cot,n,m,ans[11],bel[11],tg[N];
vector<int>fr[102];
bool in[11],ot[N];
struct P{
db x,y;
P(db x_=0.0,db y_=0.0):x(x_),y(y_){};
inline P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
inline db operator ^(const P&ky){return x*ky.y-y*ky.x;}
}ht[11],p[101];
struct Line{
P st,ed,dir;db ag;int w,x,y;
Line(){};
Line(P st_,P ed_,int w_,int x_,int y_)
{st=st_;ed=ed_;dir=ed-st;ag=atan2(dir.y,dir.x);w=w_;x=x_;y=y_;}
}le[N];
struct E{int u,v,w;}e[N];
inline int dcmp(db x){if(fabs(x)<eps) return 0;return x>0?1:-1;}
inline bool cmp(int x,int y)
{
if(dcmp(le[x].ag-le[y].ag)) return le[x].ag<le[y].ag;
return dcmp(le[x].dir^le[y].dir)>0;
//>0 而不是>=0 (必须严格大于)不然upper_bound会出锅
}
inline bool onlf(P a,Line b){return dcmp(b.dir^(a-b.st))>-1;}
void dfs(int nw,int id)
{
int i,j,st=le[nw].x,pos=le[nw].y;tg[nw]=id;
db sum=p[st]^p[pos];vi it;
for(i=1;i<=hty;++i) in[i]=onlf(ht[i],le[nw]);
for(;st^pos;){
//没有判等于所以不能lower_bound
it=--upper_bound(fr[pos].begin(),fr[pos].end(),ops(nw),cmp);
if(it==fr[pos].begin())
it=fr[pos].end();
nw=*(--it);tg[nw]=id;
for(i=1;i<=hty;++i) if(in[i]) in[i]=onlf(ht[i],le[nw]);
j=le[nw].y;sum+=p[pos]^p[j];pos=j;
}
for(i=1;i<=hty;++i) if(in[i]) bel[i]=id;
if(sum<0) ot[id]=1;
}
namespace Flow{
int cur[N],head[N],to[N],nxt[N],w[N],dep[N],tot,S,T;
inline void init()
{memset(head,0,(num+5)<<2);tot=1;}
inline void lk(int u,int v,int flw)
{
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=flw;
to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=0;
}
queue<int>que;
inline bool bfs()
{
memset(dep,0xff,(num+5)<<2);
int i,j,x;dep[S]=0;que.push(S);
for(;!que.empty();){
x=que.front();que.pop();
for(i=head[x];i;i=nxt[i]){
j=to[i];if((~dep[j])||(!w[i])) continue;
dep[j]=dep[x]+1;que.push(j);
}
}
return (dep[T]>-1);
}
int dfs(int x,int f)
{
if(x==T) return f;
int &i=cur[x],j,ss=0,res;
for(;i;i=nxt[i]){
j=to[i];if((dep[j]!=dep[x]+1)||(!w[i])) continue;
res=dfs(j,min(f-ss,w[i]));if(res){
w[i]-=res;w[i^1]+=res;ss+=res;
if(ss==f) return f;
}
}
if(!ss) dep[x]=-1;
return ss;
}
inline void sol(int mask)
{
int sz=0,re=0,i,x,y;init();
for(i=0;i<hty;++i)
if((mask>>i)&1)
{sz++;lk(S,bel[i+1],inf);}
for(i=1;i<=cot;++i)
lk(e[i].u,e[i].v,e[i].w);
for(;bfs();){
memcpy(cur,head,(num+5)<<2);
re+=dfs(S,inf);
}
ans[sz]=min(ans[sz],re);
}
}
int main(){
int i,j,x,y,z;
memset(ans,0x7f,sizeof(ans));
scanf("%d%d%d",&hty,&n,&m);cot=m<<1;
for(i=1;i<=hty;++i) scanf("%lf%lf",&ht[i].x,&ht[i].y);
for(i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
for(i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);j=(i<<1)-1;
le[j]=Line(p[x],p[y],z,x,y);fr[x].pb(j);
le[j+1]=Line(p[y],p[x],z,y,x);fr[y].pb(j+1);
}
for(i=1;i<=n;++i) sort(fr[i].begin(),fr[i].end(),cmp);
for(i=1;i<=cot;++i) if(!tg[i]) dfs(i,++num);
for(i=1;i<=cot;i+=2){
x=trs(tg[i]);y=trs(tg[i+1]);z=le[i].w;
e[i]=(E){x,y,z};e[i+1]=(E){y,x,z};
}
Flow::S=num+1;Flow::T=num+2;
for(i=(1<<hty)-1;i;--i) Flow::sol(i);
for(i=hty;i>1;--i) ans[i-1]=min(ans[i-1],ans[i]);
for(i=1;i<=hty;++i) printf("%d\n",ans[i]);
return 0;
}