【日常训练】20210304_T3_栅栏fence_平面图转对偶图/最小割树_HDU5518
题面
给一张平面图,每条边有权值,一个环的权值为其每条边的权值和,求一个权值最小的环的集合使得所有环都能通过这个集合中的环异或得到。
题解
现场得分:30/30
这道题有点难写,因为我当时写了一个平面图转对偶图,还有一个特殊线性基。很长。不过竟然调对了。
-
先平面图转对偶图,定好方向转转转就好了。然后我们在原图中希望用环权和尽可能小的环把每种环都异或出来。
-
这意味着我们对于原图中的任意两个基本的环(即转转转转出来的那些),我们希望用一个代价最小的环来将其区分开来。原图中的环对应对偶图中的最小割。所以我们现在希望在新图中用尽可能小的割来将所有点分开来。
-
跑最小割树。最小割树边权和为答案。
-
但是,说实在的,我上面写了三句话,我并不能很好地解释每句话之间是如何推导的,甚至有点怀疑。
-
我能说明几个事实:
- 我确实需要去寻找一系列环,而一个环确实对应了一个割。
- 可以利用线性基证明答案确实是寻找
个环,其中 为对偶图点数(因为原图中有一个最外圈的环和 个内圈环,我们需要 个可以任意组合,这显然 个就行了) - 最小割树确实能提供一组最小割
-
但是我不能确定为什么最小割树的那组一定是最小可行的。
代码
#include<bits/stdc++.h> #define LL long long #define U unsigned #define eps 0.000001 #define MAXN 1010 #define INF 1000000000 using namespace std; template<typename T> void Read(T &cn) { char c; int sig = 1; while(!isdigit(c = getchar())) if(c == '-') sig = 0; if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10-48+c; } else {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; } } template<typename T> void Write(T cn) { T cm = 0; int wei = 0, cx = cn%10; cn = cn/10; if(cn < 0 || cx < 0) {putchar('-'); cn = -cn; cx = -cx; } while(cn) wei++, cm = cm*10+cn%10, cn = cn/10; while(wei--) putchar(cm%10+48), cm = cm/10; putchar(cx+48); } template<typename T> void WriteL(T cn) {Write(cn); puts(""); } template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); } template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; } template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; } struct Point{ double x, y; void getit() {scanf("%lf %lf",&x,&y); } void mk(double cn, double cm) {x = cn; y = cm; } inline friend bool operator <(Point cn, Point cm) {return fabs(cn.x-cm.x)<=eps ? (fabs(cn.y-cm.y)<=eps ? 0 : cn.y < cm.y) : cn.x < cm.x; } inline friend Point operator -(Point cn, Point cm) {Point guo; guo.mk(cn.x-cm.x,cn.y-cm.y); return guo; } }; struct Graph{ struct qwe{ int a,b,ne,yong,w; void mk(int cn, int cm, int cx, int cw) {a = cn; b = cm; ne = cx; w = cw; yong = 0; } }; qwe a[MAXN*2+1]; int alen; int head[MAXN+1]; void lian(int cn, int cm, int cx) {a[++alen].mk(cn,cm,head[cn],cx); head[cn] = alen; } void liand(int cn, int cm, int cx) {lian(cn,cm,cx); lian(cm,cn,cx); } void build() {alen = 0; memset(head,0,sizeof(head)); } }; struct Paixu{ Point pos; int wei; void mk(Point cn, int cm) {pos = cn; wei = cm; } }; double chaji(Point cn, Point cm) {return cn.x*cm.y-cm.x*cn.y; } map<Point, int> M; Point pos[MAXN+1]; int m, n1, n2, t; Graph G1, G2; int zhan[MAXN+1], zlen; int ne[MAXN+1][MAXN+1], nelen[MAXN+1]; Paixu zhan2[MAXN+1]; int val[MAXN+1]; int get_Poi() {Point cn; cn.getit(); if(M.find(cn) == M.end()) M[cn] = ++n1, pos[n1] = cn; return M[cn]; } int Cmp(Paixu cn, Paixu cm) { if(cn.wei == cm.wei) return 0; if(cn.pos.x == 0 && cn.pos.y<0) return 1; if(cm.pos.x == 0 && cm.pos.y<0) return 0; if(cn.pos.x == 0) return cm.pos.x < 0; if(cm.pos.x == 0) return cn.pos.x > 0; if(cn.pos.x > 0 && cm.pos.x < 0) return 1; if(cn.pos.x < 0 && cm.pos.x > 0) return 0; return chaji(cn.pos,cm.pos) > 0; } namespace Sub1{ int pan() {return n2 >= 25 ? 0 : ((1<<n2)*G2.alen <= 40000000); } struct Jihe{ int S, he; void mk(int cn, int cm) {S = cn; he = cm; } inline friend bool operator <(Jihe cn, Jihe cm) {return cn.he == cm.he ? cn.S < cm.S : cn.he < cm.he; } }; struct Xxj{ Jihe a[100]; int n; LL he; void build(int cn) {n = cn; for(int i = 0;i<n;i++) a[i].mk(0,0); he = 0; } void cha(int cn, int cm) { for(int i = n-1;i>=0;i--) if(cn&(1<<i)) { if(!a[i].S) {a[i].mk(cn, cm); he = he+cm; return; } cn = cn ^ a[i].S; } } }xxj; Jihe zhan[MAXN+1]; int zlen; int fa[MAXN+1], you[MAXN+1]; int get_fa(int cn) {return fa[cn] == cn ? cn : fa[cn] = get_fa(fa[cn]); } int main() { zlen = 0; for(int i = 1;i<(1<<n2);i++) { for(int j = 1;j<=n2;j++) you[j] = (i&(1<<(j-1)))!=0; for(int j = 1;j<=n2;j++) fa[j] = j; int zong = 0; for(int j = 1;j<=n2;j++) if(you[j]) { zong++; for(int ij = G2.head[j];ij;ij = G2.a[ij].ne) if(you[G2.a[ij].b] && get_fa(G2.a[ij].b) != get_fa(j)) { zong--; fa[get_fa(G2.a[ij].b)] = get_fa(j); } } if(zong != 1) continue; // printf("i = %d\n",i); // for(int j = 1;j<=n2;j++) printf("you[%d] = %d\n",j,you[j]); LL lei = 0; for(int j = 1;j<=n2;j++) if(you[j]) { for(int ij = G2.head[j];ij;ij = G2.a[ij].ne) if(you[G2.a[ij].b]) lei = lei + G2.a[ij].w; } // printf("lei = %lld\n",lei); lei = -lei; for(int j = 1;j<=n2;j++) if(you[j]) lei = lei + val[j]; // printf("lei = %lld\n",lei); zhan[++zlen].mk(i, lei); } sort(zhan+1, zhan+zlen+1); xxj.build(n2); for(int i = 1;i<=zlen;i++) xxj.cha(zhan[i].S, zhan[i].he); WriteL(xxj.he); return 0; } } namespace Sub2{ Graph G; struct Flow{ int shen[MAXN+1], lst[MAXN+1], dui[MAXN+1]; LL ans; int bfs(int cn, int cm, int ctot) { for(int i = 1;i<=ctot;i++) shen[i] = ctot+1, lst[i] = G2.head[i]; int l = 0, r = 0; dui[++r] = cn; shen[cn] = 0; while(l < r) { int dang = dui[++l]; for(int i = G2.head[dang];i;i = G2.a[i].ne) { int y = G2.a[i].b; if(G2.a[i].w == 0 || shen[y] <= shen[dang]+1) continue; shen[y] = shen[dang]+1; dui[++r] = y; } } return shen[cm] != ctot+1; } int dfs(int cn, int cm, int liu) { if(cn == cm) {ans = ans + liu; return liu; } for(int &i = lst[cn];i;i = G2.a[i].ne) { int y = G2.a[i].b; if(G2.a[i].w == 0 || shen[y] != shen[cn]+1) continue; int lin = dfs(y, cm, min(liu, G2.a[i].w)); if(lin) { G2.a[i].w -= lin; G2.a[((i-1)^1)+1].w += lin; return lin; } } return 0; } void sou_fen(int cn, int fen[]) { if(fen[cn]) return; fen[cn] = 1; for(int i = G2.head[cn];i;i = G2.a[i].ne) if(G2.a[i].w) sou_fen(G2.a[i].b, fen); } LL flow(int cn, int cm, int ctot, int fen[]) { ans = 0; while(bfs(cn,cm,ctot)) while(dfs(cn,cm,INF)); for(int i = 1;i<=ctot;i++) fen[i] = 0; sou_fen(cn, fen); return ans; } }W; int zhan[MAXN+1], zhan2[MAXN+1], zlen; int fen[MAXN+1]; LL ans; void sou(int cl, int cr) { // printf("in sou : cl = %d cr = %d\n",cl,cr); if(cl == cr) return; for(int i = 1;i<=G2.alen;i++) G.a[i].w = G2.a[i].w; int lin = W.flow(zhan[cl], zhan[cr], n2, fen); for(int i = 1;i<=G2.alen;i++) G2.a[i].w = G.a[i].w; ans = ans + lin; // printf("lin = %d\n",lin); // for(int i = 1;i<=n2;i++) printf("fen[%d] = %d\n",i,fen[i]); zlen = 0; for(int i = cl;i<=cr;i++) if(fen[zhan[i]]) zhan2[++zlen] = zhan[i]; int geshu = cl+zlen-1; for(int i = cl;i<=cr;i++) if(!fen[zhan[i]]) zhan2[++zlen] = zhan[i]; for(int i = cl;i<=cr;i++) zhan[i] = zhan2[i-cl+1]; sou(cl, geshu); sou(geshu+1, cr); } int main() { for(int i = 1;i<=n2;i++) zhan[i] = i; ans = 0; sou(1, n2); WriteL(ans); return 0; } } int zuo() { Read(m); M.clear(); G1.build(); G2.build(); n1 = n2 = 0; for(int i = 1;i<=m;i++) { int bx = get_Poi(), by = get_Poi(); int bz; Read(bz); G1.liand(bx,by,bz); } // for(int i = 1;i<=n1;i++) printf("%d : (%.0lf %.0lf)\n",i,pos[i].x,pos[i].y); // for(int i = 1;i<=G1.alen;i++) printf("G1 : %d : (%d -> %d) %d\n",i,G1.a[i].a,G1.a[i].b,G1.a[i].w); for(int i = 1;i<=n1;i++) { nelen[i] = 0; for(int j = G1.head[i];j;j = G1.a[j].ne) zhan2[++nelen[i]].mk(pos[G1.a[j].b]-pos[i], j); sort(zhan2+1, zhan2+nelen[i]+1, Cmp); for(int j = 1;j<=nelen[i];j++) ne[i][j] = zhan2[j].wei; // for(int j = 1;j<=nelen[i];j++) printf("ne[%d][%d] = %d\n",i,j,ne[i][j]); } for(int i = 1;i<=G1.alen;i++) if(!G1.a[i].yong) { // printf("i = %d (%d, %d)\n",i,G1.a[i].a,G1.a[i].b); zlen = 0; int xian = G1.a[i].b, lst = G1.a[i].a; zhan[++zlen] = i; while(xian != G1.a[i].a) { int nxt = 0; for(int j = 1;j<=nelen[xian];j++) if(G1.a[ne[xian][j]].b == lst) nxt = j; if(nxt == 1) nxt = ne[xian][nelen[xian]]; else nxt = ne[xian][nxt-1]; zhan[++zlen] = nxt; lst = xian; xian = G1.a[nxt].b; } double he = 0; Point yuan = pos[G1.a[zhan[1]].a]; for(int j = 1;j<=zlen;j++) he = he + chaji(pos[G1.a[zhan[j]].a]-yuan, pos[G1.a[zhan[j]].b]-yuan); // for(int j = 1;j<=zlen;j++) printf("zhan[%d] = %d (%d->%d)\n",j,zhan[j],G1.a[zhan[j]].a,G1.a[zhan[j]].b); if(he < 0) for(int j = 1;j<=zlen;j++) G1.a[zhan[j]].yong = -1; else { n2++; val[n2] = 0; for(int j = 1;j<=zlen;j++) G1.a[zhan[j]].yong = n2, val[n2] += G1.a[zhan[j]].w; } } n2++; val[n2] = 0; for(int i = 1;i<=G1.alen;i++) if(G1.a[i].yong == -1) G1.a[i].yong = n2; for(int i = 1;i<=G1.alen;i+=2) if(G1.a[i].yong > 0 && G1.a[i+1].yong > 0) G2.liand(G1.a[i].yong, G1.a[i+1].yong, G1.a[i].w); // for(int i = 1;i<=G2.alen;i++) printf("G2 : %d : (%d -> %d) %d\n",i,G2.a[i].a,G2.a[i].b,G2.a[i].w); // for(int i = 1;i<=n2;i++) printf("val[%d] = %d\n",i,val[i]); // if(Sub1::pan()) return Sub1::main(); return Sub2::main(); } signed main() { freopen("fence.in","r",stdin); freopen("fence.out","w",stdout); Read(t); for(int i = 1;i<=t;i++) printf("Case #%d: ",i), zuo(); return 0; } /* 2 5 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 1 1 1 1 0 0 1 100 9 1 1 3 1 1 1 1 1 3 2 3 1 3 3 2 1 3 3 3 1 1 1 2 2 2 2 2 3 3 3 3 1 2 2 1 2 2 1 3 2 4 1 5 1 4 1 12 1 1 2 1 1 1 1 2 2 2 1 1 2 3 1 2 1 2 2 1 2 2 2 3 1 2 1 3 1 1 2 1 3 2 1 2 2 3 2 1 2 3 3 2 1 2 3 3 3 1 3 1 3 2 1 3 2 3 3 1 */
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步