题面
给一张平面图,每条边有权值,一个环的权值为其每条边的权值和,求一个权值最小的环的集合使得所有环都能通过这个集合中的环异或得到。
题解
现场得分: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
*/