heoi2020信号传递
状压dp
我状压学得是真烂。。。。。
考试的时候想了状压,可是一直都是在枚举位置,没有神魔实质性突破。其实这道题的关键瓶颈也在于此,状压压的是号,而不是位置。如果 \(i<=j\) 那么贡献为 \(j-i\),反之则为 \(k\times (i+j)\)。于是只需知道 i 点前都是谁就可计算 i 的贡献,也就是说去压号。
现在暴力 dp 很明显了,关键又在于\(1<<23 \times 23\) 会爆空间啊。想一想 i 对自己的贡献一定是 0 啊,那么预处理贡献时点集中不需要包括 i 本身,也就是说如果此位 \(>=i\),他实际上就是 x+1,\(1<<22\)可以承受。
#include<bits/stdc++.h>
#define re register
#define f() cout<<"fuck"<<endl
using namespace std;
int n,m,k;
int ji[23][23],dp[1<<23],size[1<<23],biao[1<<23],zxb[23][1<<22];
inline void fr(void){freopen("c.in","r",stdin);}
inline void sc(void){scanf("%d%d%d",&n,&m,&k);}
namespace AYX
{ inline void work(void)
{ int x;scanf("%d",&x);--x;
for(re int i=2;i<=n;++i)
{ int y;scanf("%d",&y);--y;
++ji[x][y];x=y;
}
biao[0]=-1;
for(re int i=1;i<(1<<m);++i)biao[i]=biao[i>>1]+1,size[i]=size[i>>1]+(i&1);
for(re int i=0;i<m;++i)
{ for(re int j=0;j<m;++j)if(i^j)zxb[i][0]+=k*ji[j][i]-ji[i][j];
for(re int j=1;j<(1<<(m-1));++j)
{ int y=j&-j,z=biao[y];
z+=(z>=i);
zxb[i][j]=zxb[i][j^y]+ji[i][z]*(k+1)+ji[z][i]*(1-k);
}
}
int y;
for(re int i=1;i<(1<<m);++i)
{ for(dp[i]=0x3f3f3f3f,x=i;y=x&-x;x^=y)
{ int z=biao[y],w=i^y;
dp[i]=min(dp[i],dp[i^y]+zxb[z][w&y-1|w>>z+1<<z]*size[i]);
}
}
printf("%d\n",dp[(1<<m)-1]);
}
inline short main()
{sc();work();return 0;}
}
signed main()
{return AYX::main();}