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();}
posted @ 2021-10-01 19:50  -zxb-  阅读(45)  评论(0编辑  收藏  举报