Bzoj2965 保护古迹

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 366  Solved: 155

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个点互不相同。

 

Source

 

数学问题 计算几何 

图论 网络流 最小割

大脑洞题

 

想题十分钟,写题五小时系列。

把给出的图转成对偶图,选权值和最小的边围住古迹就相当于用最小的代价割断古迹所在区块和最外界的联系——可以转化成最小割问题。

所以基本思路就是:求出对偶图,根据原图中的相邻关系在对偶图上连边,权值等于原图上两区块共用边的权值,从源点向要保护的古迹连边,从“最外面”的点向汇点连边,求最小割。

并不能确定要保护哪些古迹,所幸p<=10,可以暴力枚举所有状态。

剩下的问题就是求对偶图了。

将所有的边拆成两条双向边,从一条未遍历的边出发,一路“左转”(找逆时针旋转角度最大的未走过的边走),直到回到自己,这些边圈出来的就是原图上的一个区块。

实现起来神™麻烦,写出了一个大概的框架就无从下手了,然后找了风格最像的popoQQQ大爷的代码开始借鉴模仿,STL和指针什么的各种不会用,自信写完以后无限爆炸。

放弃了探索未知的指针领域,开始换写法照抄,又改了一个多小时,终于不炸了,开始无限WA。

祭出了久违的标准代码比对法,最后发现有一处的网络流加边没加反向弧……

 

  1 /*by SilverN*/
  2 #include<algorithm>
  3 #include<iostream>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<cmath>
  7 #include<vector>
  8 #include<queue>
  9 using namespace std;
 10 const double pi=acos(-1.0);
 11 const int INF=0x3f3f3f3f;
 12 const int mxn=80010;
 13 int read(){
 14     int x=0,f=1;char ch=getchar();
 15     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 16     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
 17     return x*f;
 18 }
 19 int n,m;
 20 namespace Graph{
 21     struct edge{int v,nxt,f;}e[mxn<<1];
 22     int hd[mxn],mct=1;
 23     void add_edge(int u,int v,int w){
 24         e[++mct].v=v;e[mct].nxt=hd[u];e[mct].f=w; hd[u]=mct;return;
 25     }
 26     void insert(int u,int v,int w){
 27         add_edge(u,v,w);add_edge(v,u,0);return;
 28     }
 29     queue<int>q;
 30     int d[mxn];
 31     int S,T;
 32     bool BFS(){
 33         memset(d,0,sizeof d);
 34         d[S]=1;
 35         q.push(S);
 36         while(!q.empty()){
 37             int u=q.front();q.pop();
 38             for(int i=hd[u];i;i=e[i].nxt){
 39                 int v=e[i].v;
 40                 if(e[i].f && !d[v]){
 41                     d[v]=d[u]+1;
 42                     q.push(v);
 43                 }
 44             }
 45         }
 46         return d[T];
 47     }
 48     int DFS(int u,int lim){
 49         if(u==T)return lim;
 50         int f=0,tmp;
 51         for(int i=hd[u],v;i;i=e[i].nxt){
 52             v=e[i].v;
 53             if(d[v]==d[u]+1 && e[i].f && (tmp=DFS(v,min(lim,e[i].f)))){
 54                 e[i].f-=tmp;  e[i^1].f+=tmp;
 55                 lim-=tmp;   f+=tmp;
 56                 if(!lim)return f;
 57             }
 58         }
 59         d[u]=0;
 60         return f;
 61     }
 62     int Dinic(){
 63         int res=0;
 64         while(BFS())res+=DFS(S,0x3f3f3f3f);
 65         return res;
 66     }
 67     void Reset(){
 68         for(int i=2;i<=mct;i+=2){  
 69             e[i].f+=e[i^1].f;
 70             e[i^1].f=0;
 71         }
 72         return;
 73     }  
 74     void Ban(int x){  
 75         for(int i=mct-2*x+1;i<=mct;i+=2) e[i].f=0;
 76         return;
 77     }
 78 }
 79 //
 80 namespace Geometry{
 81     struct point{
 82         double x,y;
 83         point operator + (point b){return (point){x+b.x,y+b.y};}
 84         point operator - (point b){return (point){x-b.x,y-b.y};}
 85         double operator * (point b){return x*b.x+y*b.y;}
 86         bool operator < (point b)const{
 87             return x<b.x || (x==b.x && y<b.y);
 88         }
 89     }a[mxn],h[mxn];
 90     typedef point Vector;
 91     int cnt=0;//对偶图结点 
 92     double Cross(point a,point b){
 93         return a.x*b.y-a.y*b.x;
 94     }
 95     double dist(point a,point b){
 96         return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
 97     }
 98     struct seg{
 99         int x,y;
100         double len(){return dist(a[x],a[y]);}
101     }se[mxn];
102     struct Line{
103         Line *par;
104         int u,v,w;
105         double angle;
106         int belong;//所属对偶图点 
107         bool operator < (Line b)const{//角度按逆时针排序 
108             return angle<b.angle;
109         }
110     };
111     bool angcmp(Line *x,Line *y){return x->angle < y->angle;}
112     vector<seg>sg;//搜索时保存的路径 
113     vector<Line*>ve[mxn];//从每个点出发的边 
114     void add_Line(int x,int y,int w){
115         Line *tmp1=new Line;
116         (*tmp1).u=x;(*tmp1).v=y;(*tmp1).w=w;(*tmp1).belong=0;
117         (*tmp1).angle=atan2(a[y].y-a[x].y,a[y].x-a[x].x);
118         Line *tmp2=new Line;
119         (*tmp2).u=y;(*tmp2).v=x;(*tmp2).w=w;(*tmp2).belong=0;
120         (*tmp2).angle=atan2(a[x].y-a[y].y,a[x].x-a[y].x);
121         tmp1->par=tmp2;
122         tmp2->par=tmp1;
123         ve[x].push_back(tmp1);
124         ve[y].push_back(tmp2);
125         return;
126     }
127     bool vis[mxn];
128     void GDFS(int u,Line *from){//搜对偶图 
129 //        printf("GDFS:u:%d from.v:%d\n",u,from->v);
130         from->belong=cnt;
131         sg.push_back((seg){from->par->v,u});//存线段
132         vector<Line*>::iterator it;
133         it=lower_bound(ve[u].begin(),ve[u].end(),from->par,angcmp);
134         it++;
135         if(it==ve[u].end())it=ve[u].begin();//顺时针找第一条没经过的边 
136         if((*it)->belong)return; 
137         GDFS((*it)->v,*it);
138         return;
139     }
140     double calc(){//算面积 
141         double res=0;
142         vector<seg>::iterator it;
143         for(it=sg.begin();it!=sg.end();it++){
144             res+=Cross(a[(*it).y],a[(*it).x]);
145         }
146         return res/2;
147     }
148     double getangle(point p1,point p2){
149         return acos( (p1*p2)/sqrt(p1*p1)/sqrt(p2*p2) );
150     }
151     bool check(point c){
152         static const point v=(point){3.141592653,2.718281828};
153         int tmp=0;
154         vector<seg>::iterator it;
155         point p=c+v;
156         for(it=sg.begin();it!=sg.end();it++){
157             double C1=Cross(c-a[(*it).x],p-a[(*it).x]);
158             double C2=Cross(c-a[(*it).y],p-a[(*it).y]);
159             if(C1*C2<0 && getangle(v,a[(*it).x]-c)+getangle(v,a[(*it).y]-c)<=pi )
160                 tmp++;
161         }
162         return tmp&1;
163     }
164 }
165 //
166 int p;
167 int inarea[mxn];//古迹所属区域 
168 double mxarea=0;
169 int ans[mxn];
170 int main(){
171 //    freopen("in.txt","r",stdin);
172     using namespace Graph;
173     using namespace Geometry;
174     int i,j,u,v,w;
175     S=0;T=4100;cnt=0;
176     p=read();n=read();m=read();
177     for(i=1;i<=p;i++){scanf("%lf%lf",&h[i].x,&h[i].y);}
178     for(i=1;i<=n;i++){scanf("%lf%lf",&a[i].x,&a[i].y);}
179     for(i=1;i<=m;i++){
180         u=read();v=read();w=read();
181         add_Line(u,v,w);
182     }
183     for(i=1;i<=n;i++)sort(ve[i].begin(),ve[i].end(),angcmp);
184     //
185     for(i=1;i<=n;i++){//枚举起点 
186         vector<Line*>::iterator it;
187         for(it=ve[i].begin();it!=ve[i].end();it++){
188             if(!(*it)->belong){
189                 sg.clear();
190                 ++cnt;
191                 GDFS((*it)->v,*it);
192                 double res=calc();
193                 if(res<0){
194                     insert(cnt,T,INF);
195                     continue;
196                 }
197                 for(j=1;j<=p;j++){//枚举点判断是否在区间内 
198                     if(check(h[j])){
199                         inarea[j]=cnt; 
200                     }
201                 }
202             }
203         }
204     }
205     //平面图转对偶图
206     for(i=1;i<=n;i++){//对偶图相邻点连边 
207         vector<Line*>::iterator it;
208         for(it=ve[i].begin();it!=ve[i].end();it++){
209             insert((*it)->belong,(*it)->par->belong,(*it)->w);
210         }
211     }
212     memset(ans,0x3f,sizeof ans);
213     int ed=1<<p;
214     for(i=1;i<ed;i++){//枚举要保护的古迹集 
215         int num=0;
216         for(j=1;j<=p;j++){
217             if(i&(1<<(j-1))){
218                 num++;
219                 insert(S,inarea[j],INF);
220             }
221         }
222         int res=Dinic();
223         ans[num]=min(ans[num],res);
224         Reset();
225         Ban(num);
226     }
227     for(i=1;i<=p;i++){
228         printf("%d\n",ans[i]);
229     }
230     return 0;
231 }

 

posted @ 2017-04-06 21:44  SilverNebula  阅读(182)  评论(0编辑  收藏  举报
AmazingCounters.com