Bzoj2965 保护古迹
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
-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
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 }