zoukankan      html  css  js  c++  java
  • 【正睿2019暑假集训】正睿891 蔡老板与豪宅

    题目链接

    前置知识:fwt

    提前约定:设(S_i)表示装修队(i)能装修的点集;(E_i)表示装修队(i)能装修的边集,也就是(S_i)这个点集所生成的完全图的边集。

    因为装修队的数量(m)很小,考虑状压DP。设(dp[s])表示已经请了集合(s)里的这些装修队,能实现的最大花费。

    考虑预处理出(f[s])表示集合(s)里的装修队,能装修的边集的并的大小。也就是(f[s]=left|igcup_{iin s}E_i ight|)。那我们的DP就可以有如下转移:

    [dp[s]=max_{iin s}{dp[ssetminus i]+ ext{cost}_i(f[s]-f[ssetminus i])} ]

    其中(i)表示枚举新添加了哪个装修队。( ext{cost}_i(e))表示装修队(i),新装修了(e)条边,所收取的费用,也就是( ext{cost}_i(e)=(a_ie^2+b_ie+c_i)mod2^{32})

    DP的时间复杂度是(O(2^mm)),可以接受。于是问题转化为求(f[s])

    考虑容斥:

    [egin{align} f[s]&=left|igcup_{iin s}E_i ight|\ &=sum_{s'subseteq s}(-1)^{|s'|+1}left|igcap_{iin s'}E_i ight| end{align} ]

    发现(left|igcap_{iin s'}E_i ight|)(也就是“边集交”)的大小,之和点集交有关。具体来说,设(g[s])表示集合(s)里的装修队,能装修的点集的交的大小,也就是(g[s]=left|igcap_{iin s}S_i ight|)。那么(left|igcap_{iin s'}E_i ight|={g[s]choose 2})

    如果知道了(g),那么求(f),就相当于做高维前缀和,可以用fwt or卷积,在(O( ext{len}log ext{len})=O(2^mm))的时间里实现。于是问题进一步转化为求(g)

    因为点太多了,枚举每个(s),再求“点集交”显然不行。考虑每个点对(g)的贡献。每个点(u),会对应一个能装修到它的装修队集合(t_u)。发现如果(s)(t_u)的子集,则点(u)会对(g[s])产生(1)的贡献:表示点(u)在集合(s)的“点集交”里。于是我们可以构造出一个(g'[s]=sum_{u=1}^{n}[t_u=s]),然后对(g')高维后缀和(fwt and 卷积),就可以得到(g)了。

    至此,我们以(O(2^mm))的时间复杂度解决了本题。回顾一下过程:先求出“点集交”(g)(考虑每个点的贡献,fwt and卷积),再求出“边集并”(f)(容斥),最后做一个简单的状压DP。

    参考代码:

    //problem:ZR891
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
    template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
    
    const int MAXN=2e5,MAXM=20;
    int n,m;
    struct CostCalculater{
    	uint a,b,c;
    }cost[MAXM+5];
    vector<int>nodes[MAXM+5];
    int companies[MAXN+5],g[1<<MAXM];
    ll f[1<<MAXM],dp[1<<MAXM];
    int bitcnt[1<<MAXM];
    /*
    g[s]: s里的这些装修队,它们的点集交的大小
    f[s]: s里的这些装修队,它们的边集并的大小
    dp[s]: 已经请了s里的这些装修队,能实现的最大花费
    */
    template<typename T>
    void fwt_and(T* a,int n,T flag){
    	for(int i=1;i<n;i<<=1){
    		for(int j=0;j<n;j+=(i<<1)){
    			for(int k=0;k<i;++k){
    				a[j+k]+=a[j+k+i]*flag;
    			}
    		}
    	}
    }
    template<typename T>
    void fwt_or(T* a,int n,T flag){
    	for(int i=1;i<n;i<<=1){
    		for(int j=0;j<n;j+=(i<<1)){
    			for(int k=0;k<i;++k){
    				a[j+k+i]+=a[j+k]*flag;
    			}
    		}
    	}
    }
    
    int main() {
    	cin>>n>>m;
    	for(int i=1;i<=m;++i){
    		cin>>cost[i].a>>cost[i].b>>cost[i].c;
    		int sz;cin>>sz;
    		nodes[i].resize(sz);
    		for(int j=0;j<sz;++j){
    			cin>>nodes[i][j];
    			companies[nodes[i][j]]|=(1<<(i-1));
    		}
    	}
    	for(int i=1;i<=n;++i)
    		g[companies[i]]++;
    	fwt_and(g,1<<m,1);
    	g[0]=0;
    	for(int i=1;i<(1<<m);++i){
    		bitcnt[i]=bitcnt[i>>1]+(i&1);
    		f[i]=(ll)((bitcnt[i]&1)?1:-1)*g[i]*(g[i]-1)/2;
    	}
    	fwt_or(f,1<<m,1LL);
    	for(int i=1;i<(1<<m);++i){
    		for(int j=1;j<=m;++j)if((i>>(j-1))&1){
    			uint e=(f[i]-f[i^(1<<(j-1))]) % (1LL<<32);
    			uint c=(cost[j].a*e*e+cost[j].b*e+cost[j].c);
    			ckmax(dp[i],dp[i^(1<<(j-1))]+c);
    		}
    	}
    	cout<<dp[(1<<m)-1]<<endl;
    	return 0;
    }
    
  • 相关阅读:
    如何下载无水印的抖音视频?
    @valid和自定义异常
    Centos7查看外网ip,yum安装的curl无法正常使用
    ElasticSearch安装
    Redis的主从架构+哨兵模式
    Redis的持久化方式
    Nacos 注册中心集群搭建
    kafka安装流程
    WinUI 3学习笔记(1)—— First Desktop App
    .NET 5学习笔记(12)——WinUI 3 Project Reunion 0.5
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13297534.html
Copyright © 2011-2022 走看看