[bzoj2965]保护古迹

2965: 保护古迹

Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 300 Solved: 117
[Submit][Status][Discuss]
Description

  某校由于历史悠久,校园中有大量的名胜古迹。为了更好地保护这些古迹,学校决定用篱笆将这些古迹围起来。
  现在已知有p个地点的古迹需要保护。这些古迹可以看做二维平面上的整数点。有n个点可以作为篱笆的端点,这些端点的坐标也为二维平面上的整数。端点用1到n的整数编号。
  有m对端点之间可以修建篱笆。用(u,v,w)描述一段可以修建的篱笆,表示端点u和端点v之间可以花费w的代价修建一段。篱笆都看做直线段。为了方便设计,这些可以修建的篱笆都是不会相交的(只会在端点处相交)。
  将一个古迹围起来是指存在一个由篱笆构成的简单多边形,这个古迹在该多边形内部。
  由于经费问题,学校希望修建篱笆的花费最小。你需要输出将至少1个,2个,…,p个古迹围起来的最小花费。

Input

  第一行包含三个正整数p,n,m表示古迹的个数,端点个数和可以修建的篱笆条数。
  接下来p行,每行包含两个整数,表示每个古迹的坐标。
  接下来n行,每行包含两个整数,表示每个端点的坐标。这些端点按照输入的顺序依次用1到n的整数编号。
  最后m行,每行包含三个非负整数u,v,w,表示可以在端点u和端点v之间花w的代价修建一段篱笆。

Output

  输出p行,分别表示将至少1个,2个,…,p个古迹围起来的最小花费。

Sample Input

3 9 15

-2 2

2 1

2 -1

3 0

3 2

1 2

-1 3

-3 3

-2 1

1 0

2 -2

2 -3

1 2 20

1 7 40

1 8 10

1 9 100

2 3 50

3 4 1000

3 7 10

4 5 10

4 6 10

4 7 1000

5 6 10

6 7 1000

7 8 120

7 9 10

8 9 10

Sample Output

30

100

140

HINT

这里写图片描述

 对于100%的数据,n≤100, m≤C(n,2),p≤10。所有坐标位置的两维绝对值不超过109,u,v不超过n,w不超过106。

  保证可以修建的篱笆不会经过古迹。保证可以修建的两段篱笆不会在非端点处相交或重合。保证至少存在一种方案可以包围所有古迹。保证n个点互不相同。

首先我们可以看出来,题目给出的是一个平面图。
假设我们已经把这个平面图转化成了对偶图,那么求保护一些古迹的最小代价就做一遍最小割。
但是现在我们不知道要保护哪些古迹
数据范围中p<=10!!
这样我们暴力枚举一下选哪些古迹就好了。
再来说一下怎样转化成对偶图
我们从一个开始bfs,每次走与它相连的没有走过的边,从这些边中选择最左边的一个往后走,这样我们就能得到每一个区域。
现在我们需要做的就是看看这些古迹在哪个区域内。
问题就变成了判断一个点是否在多边形内。
可以用随机射线法。(但其实题目中说是简单多边形,也就是不会有凹多边形,所以直接看看这个点是不是在多边形边的一侧就行了。)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define T sum+2
#define LL long long
#define inf 0x7fffffffffffffff
const int N=1100;
const int M=2000010;
bool use[N],map[N][N];
LL f[N<<1],g[N];
int o,n,m,tot,point[N],next[M],stack[N],top,sum,circle[N],belong[N][2],p[N],cur[N],dis[N],gap[N],pre[N];
struct Point{LL x,y;}pi[N],ai[N],bi[N][N];
struct Line{int st,en;LL va;Point v;}line[N];
struct S{int st,en;LL va;}aa[M];
inline LL Cross(Point x,Point y){return x.x*y.y-x.y*y.x;}
Point operator + (Point x,Point y){return (Point){x.x+y.x,x.y+y.y};}
Point operator - (Point x,Point y){return (Point){x.x-y.x,x.y-y.y};}
Point operator * (Point x,LL y){return (Point){x.x*y,x.y*y};}
inline void add_1(int x,int y,LL z){
    next[++tot]=point[x];point[x]=tot;
    line[tot].st=x;line[tot].en=y;line[tot].va=z;line[tot].v=ai[x]-ai[y];
    next[++tot]=point[y];point[y]=tot;
    line[tot].st=y;line[tot].en=x;line[tot].va=z;line[tot].v=ai[y]-ai[x];
}
inline void dfs(int x){
    bool flag;
    int i,now,j;
    use[x]=true;
    for(i=point[x];i;i=next[i])
      if(!map[line[i].en][i]){
        stack[top=1]=i;
        flag=true;
        while(line[stack[top]].en!=x){
            for(now=0,j=point[line[stack[top]].en];j;j=next[j])
              if((Cross(line[j].v,line[stack[top]].v)>0LL)&&(!now||Cross(line[j].v,line[now].v)>0LL)) now=j;
            if(!now){
                flag=false;
                break;
            }
            stack[++top]=now;
        }
        if(!flag) continue;
        for(++sum,j=1;j<=top;++j){
            bi[sum][++circle[sum]]=ai[line[stack[j]].en];
            map[line[stack[j]].en][stack[j]]=true;
            if(!belong[(stack[j]+1)>>1][0]) belong[(stack[j]+1)>>1][0]=sum;
            else belong[(stack[j]+1)>>1][1]=sum;
        }
      }
    for(i=point[x];i;i=next[i])
      if(!use[line[i].en]) dfs(line[i].en);
}
inline bool check(int x,int y){
    int i,j;
    LL now1,now2;
    now1=Cross(pi[x]-bi[y][circle[y]],bi[y][1]-bi[y][circle[y]]);
    for(i=2;i<=circle[y];++i){
        now2=Cross(pi[x]-bi[y][i-1],bi[y][i]-bi[y][i-1]);
        if((now1>0LL&&now2<0LL)||(now1<0LL&&now2>0LL)) return false;
    }
    return true;
}
inline void add_2(int x,int y,LL z){
    next[++tot]=point[x];point[x]=tot;
    aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
    next[++tot]=point[y];point[y]=tot;
    aa[tot].st=y;aa[tot].en=x;aa[tot].va=z;
}
inline LL ISAP(int ss,int tt){
    bool f;
    int i,u=ss,y;
    LL minn,ans=0;
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for(i=1;i<=T;++i) cur[i]=point[i];
    gap[0]=T;
    while(dis[ss]<T){
        f=false;
        for(i=cur[u];i;i=next[i])
          if(aa[i].va>0&&dis[aa[i].en]+1==dis[u]){
            f=true;cur[u]=i;break;
          }
        if(f){
            pre[u=aa[i].en]=i;
            if(u==tt){
                minn=inf;
                for(i=u;i!=ss;i=aa[pre[i]].st)
                  minn=min(minn,aa[pre[i]].va);
                for(i=u;i!=ss;i=aa[pre[i]].st){
                    aa[pre[i]].va-=minn;
                    aa[pre[i]^1].va+=minn;
                }
                ans+=minn;u=ss;
            }
        }
        else{
            --gap[dis[u]];
            if(!gap[dis[u]]) return ans;
            y=T+1;cur[u]=point[u];
            for(i=point[u];i;i=next[i])
              if(aa[i].va>0) y=min(y,dis[aa[i].en]);
            ++gap[dis[u]=y+1];
            if(u!=ss) u=aa[pre[u]].st;
        }
    }
    return ans;
}
inline LL calc(int x){
    int i,j;
    memset(point,0,sizeof(point));
    for(tot=1,i=1;i<=o;++i)
      if(x&(1<<(i-1))) add_2(1,p[i]+1,inf);
    for(i=1;i<=m;++i)
      if(belong[i][0]){
        if(belong[i][1]) add_2(belong[i][0]+1,belong[i][1]+1,line[i<<1].va);
        else add_2(belong[i][0]+1,T,line[i<<1].va);
      }
    return ISAP(1,T);
}
int main(){
    LL z;
    int i,j,x,y;
    scanf("%d%d%d",&o,&n,&m);
    for(i=1;i<=o;++i) scanf("%lld%lld",&pi[i].x,&pi[i].y);
    for(i=1;i<=n;++i) scanf("%lld%lld",&ai[i].x,&ai[i].y);
    for(i=1;i<=m;++i){
        scanf("%d%d%lld",&x,&y,&z);
        add_1(x,y,z);
    }
    for(i=1;i<=n;++i)
      if(!use[i]) dfs(i);
    for(i=1;i<=o;++i)
      for(j=1;j<=sum;++j)
        if(check(i,j)){
            p[i]=j;
            break;
        }
    memset(f,127/3,sizeof(f));
    memset(g,127/3,sizeof(g));
    for(i=1;i<(1<<o);++i){
        f[i]=calc(i);
        for(j=i;j;j=(j-1)&i)
          f[i]=min(f[i],f[j]+f[j^i]);
        for(x=0,j=i;j;j>>=1)
          if(j&1) ++x;
        g[x]=min(g[x],f[i]);
    }
    for(i=o-1;i;--i) g[i]=min(g[i],g[i+1]);
    for(i=1;i<=o;++i) printf("%lld\n",g[i]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值