1. 程式人生 > >Codechef:Find a special connected block/CONNECT(斯坦納樹)

Codechef:Find a special connected block/CONNECT(斯坦納樹)

傳送門

題解:
這道題真tm噁心啊。

顏色數小就直接斯坦樹了,顏色大的話則考慮給每個顏色隨機對映到 [ 1 , K ] [1,K] 的顏色中,這樣的正確率就是 K

! K K \frac{K!}{K^K} ,概率大概在0.006。多隨機幾次就過了。

#include <bits/stdc++.h>
using namespace std;

const int RLEN=1<<
18|1; inline char nc() { static char ibuf[RLEN],*ib,*ob; (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin)); return (ib==ob) ? -1 : *ib++; } inline int rd() { char ch=nc(); int i=0,f=1; while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();} while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'
; ch=nc();} return i*f; } const int N=17, M=(1<<7)+20, B=1e5+50, inf=1e9, T=550; const int fx[]={0,0,-1,1}; const int fy[]={1,-1,0,0}; int n,m,tot,k,bin[N],ans=inf; #define gi(x,y) ((x-1)*m+y) int f[N*N][M],c[N*N],v[N*N],w[N*N]; int g[N*N],vt[N*N*8],nt[N*N*8],ec; int q[B],exi[N*N],r; inline void add(int x,int y) {nt[++ec]=g[x]; g[x]=ec; vt[ec]=y;} struct data { int x,y,w; data(int x,int y,int w) : x(x),y(y),w(w) {} }; inline unsigned int unit() { static unsigned int x=2333; x^=(x<<5);x^=(x>>17);x^=(x<<13); return x; } inline void solve() { for(int i=1;i<=tot;i++) for(int sta=0;sta<bin[k];sta++) f[i][sta]=inf; map <int,int> id; for(int i=1;i<=tot;i++) if(c[i]>0) { if(!id.count(c[i])) id[c[i]]=unit()%k; v[i]=id[c[i]]; } for(int i=1;i<=tot;i++) if(~c[i]) f[i][0]=w[i]; for(int sta=0;sta<bin[k];sta++) { for(int i=1;i<=tot;i++) if(~c[i]) { if(c[i] && (sta&bin[v[i]])) f[i][sta]=min(f[i][sta],f[i][sta^bin[v[i]]]); for(int s=sta;s>sta^s;s=(s-1)&sta) f[i][sta]=min(f[i][sta],f[i][s]+f[i][sta^s]-w[i]); } r=0; for(int i=1;i<=tot;i++) if(~c[i]) if(f[i][sta]<ans) q[++r]=i, exi[i]=1; for(int i=1;i<=r;i++) { int u=q[i], uw=f[u][sta]; exi[u]=0; for(int e=g[u];e;e=nt[e]) { int v=vt[e]; if(~c[v] && f[v][sta]>uw+w[v]) { f[v][sta]=uw+w[v]; if(!exi[v] && f[v][sta]<ans) exi[v]=1, q[++r]=v; } } } } for(int i=1;i<=tot;i++) ans=min(ans,f[i][bin[k]-1]); } int main() { n=rd(), m=rd(), k=rd(); tot=n*m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) c[gi(i,j)]=rd(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) w[gi(i,j)]=rd(); for(int i=0;i<=k;i++) bin[i]=1<<i; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(~c[gi(i,j)]) for(int z=0;z<4;z++) { int i2=i+fx[z], j2=j+fy[z]; if(i2<1 || i2>n || j2<1 || j2>m || !~c[gi(i2,j2)]) continue; add(gi(i,j),gi(i2,j2)); } for(int i=T;i;i--) solve(); printf("%d\n",(ans==inf) ? -1 : ans); }