【正睿2019暑假集训】正睿891 蔡老板与豪宅
提前约定:设\(S_i\)表示装修队\(i\)能装修的点集;\(E_i\)表示装修队\(i\)能装修的边集,也就是\(S_i\)这个点集所生成的完全图的边集。
因为装修队的数量\(m\)很小,考虑状压DP。设\(dp[s]\)表示已经请了集合\(s\)里的这些装修队,能实现的最大花费。
考虑预处理出\(f[s]\)表示集合\(s\)里的装修队,能装修的边集的并的大小。也就是\(f[s]=\left|\bigcup_{i\in s}E_i\right|\)。那我们的DP就可以有如下转移:
其中\(i\)表示枚举新添加了哪个装修队。\(\text{cost}_i(e)\)表示装修队\(i\),新装修了\(e\)条边,所收取的费用,也就是\(\text{cost}_i(e)=(a_ie^2+b_ie+c_i)\bmod2^{32}\)。
DP的时间复杂度是\(O(2^mm)\),可以接受。于是问题转化为求\(f[s]\)。
考虑容斥:
发现\(\left|\bigcap_{i\in s'}E_i\right|\)(也就是“边集交”)的大小,之和点集交有关。具体来说,设\(g[s]\)表示集合\(s\)里的装修队,能装修的点集的交的大小,也就是\(g[s]=\left|\bigcap_{i\in s}S_i\right|\)。那么\(\left|\bigcap_{i\in s'}E_i\right|={g[s]\choose 2}\)。
如果知道了\(g\),那么求\(f\),就相当于做高维前缀和,可以用fwt or卷积,在\(O(\text{len}\log \text{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;
}