zoukankan      html  css  js  c++  java
  • [BZOJ1495][NOI2006]网络收费

    bzoj
    luogu

    sol

    观察系数(k)可以发现一个很有趣的规律:系数恰好可以视作对相对数量较少的用户个数。(若两者数量相等则视作(B)少)
    比如说,表格的第二行,当(i)的付费方式为(A)(j)的付费方式为(B),且(n_A<n_B)时,产生的代价是一倍的(F_{i,j})。可以当作是对相对数量较少的(i)用户收了(F_{i,j}),对(j)用户没有收费。
    那么一个用户产生的费用就可以由他自己选取的类型以及他的所有祖先节点的状态((A)多还是(B)多)唯一确定。
    这样子的话就可以做一个(dp)了。设(dp[x][y][z])表示(dp)到树上的第(x)个节点,这个节点中包含(y)(B),然后祖先节点的状态是(z)的最小代价。底层状态就是(x)为叶子,这个时候可以(O(log n))算一下这一个用户的代价。否则可以按照背包转移。
    发现这个(dp)是三维的。但是考虑一下,随着(x)的深度增加,(z)会多一位而(y)的上界减小一半,也就是说对于一个(x)(y,z)的状态数量的乘积是一定的,而且这个数量也就是(O(2^n))的。
    那么就可以把(y,z)两维状态压进一维,然后记忆化一下就可以了。复杂度是(O(2^{2n}))

    code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi()
    {
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 2500;
    const int inf = 2e9;
    int n,all,b[N],c[N],f[N][N],L[N],R[N],dp[N][N],ans=inf;
    void build(int x,int l,int r)
    {
    	L[x]=l;R[x]=r;if (l==r) return;
    	int mid=l+r>>1;
    	build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    }
    int dfs(int x,int y,int z,int dep)
    {
    	if (dp[x][(y<<dep)|z]!=-1) return dp[x][(y<<dep)|z];
    	if (dep==n)
    	{
    		int sum=c[x-all+1]*(y!=b[x-all+1]);
    		for (int u=x,d=n-1;d>=0;--d,u>>=1)
    			sum+=(f[x-all+1][R[u^1]]-f[x-all+1][L[u^1]-1])*(y!=((z>>d)&1));
    		return dp[x][(y<<dep)|z]=sum;
    	}
    	else
    	{
    		int res=inf,zt=y*2>R[x]-L[x]+1;
    		for (int i=max(0,y-(R[x<<1|1]-L[x<<1|1]+1));i<=min(y,R[x<<1]-L[x<<1]+1);++i)
    			res=min(res,dfs(x<<1,i,z|(zt<<dep),dep+1)+dfs(x<<1|1,y-i,z|(zt<<dep),dep+1));
    		return dp[x][(y<<dep)|z]=res;
    	}
    }
    int main()
    {
    	n=gi();all=1<<n;
    	for (int i=1;i<=all;++i) b[i]=gi();
    	for (int i=1;i<=all;++i) c[i]=gi();
    	for (int i=1;i<=all;++i)
    		for (int j=i+1;j<=all;++j)
    			f[i][j]=f[j][i]=gi();
    	for (int i=1;i<=all;++i)
    		for (int j=1;j<=all;++j)
    			f[i][j]+=f[i][j-1];
    	build(1,1,all);memset(dp,-1,sizeof(dp));
    	for (int i=0;i<=all;++i)
    		ans=min(ans,dfs(1,i,0,0));
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    hisi3516/3519开发(二)—xshell连接串口
    linux svn使用
    IdentityServer4 源码介绍
    想写博客
    # VS2019 快捷键插入当前时间
    # 使用 vscode markdown 遇到的问题
    # 学Vue
    teXt使用
    Linux基础
    NopCommerce(Core)学习目录
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8583232.html
Copyright © 2011-2022 走看看