题解 H. Fly "蔚来杯"2022牛客暑期多校训练营1

传送门

觉得题解写有点模糊,自己写一份


【大意】

给定 \(n\) 个正整数 \(a_1, a_2, \cdots, a_n\) ,和 \(k\) 个限制 \((b_1, c_1), (b_2, c_2), \cdots, (b_k, c_k)\)

求有多少个不同的有序数组 \(x_1, x_2, \cdots, x_n\) 使得其满足以下两个限制:

\(\begin{cases}\begin{aligned}\sum_{i=1}^n a_ix_i&\leq m\\x_{b_i}\&2^{c_i}&=0&\forall i=1, 2, 3, \cdots, k\end{aligned}\end{cases}\)


【分析】

显然,\(x_{b_i}\&2^{c_i}=0\) 表示 \(x_{b_i}\) 的第 \(c_i\) 位为 \(0\) ,这也启示我们可以从二进制位的角度考虑做法

\(x_i\cdot a_i\) 对求和式的贡献,可以独立地拆解为 \(2^0\cdot x_{i, 0}\cdot a_i, 2^1\cdot x_{i, 1}\cdot a_i, \cdots, 2^k\cdot x_{i, k}\cdot a_k, \cdots\) 分别考虑

由于 \(\forall i\to a_i>0\)\(m<2^{60}\) ,故 \(x\) 不可能有超过 \(60\) 位为 \(1\)

因此问题等价于从 \(2^0\cdot a_1, 2^1\cdot a_1, \cdots, 2^{59}\cdot a_1, 2^0\cdot a_2, \cdots, 2^{59}\cdot a_n\)\(60n\) 个物品中,扣除所有 \(2^{c_i}\cdot a_{b_i}\)\(k\) 个物品后,查询体积不超过 \(m\) 的背包方案数

注意到每个 \(a_i\) 前面乘的系数都是 \(2\) 的整幂次,若我们对进行背包的物品,按系数的 \(2\) 的幂次进行分类,从小到大进行背包;则在 \(2^t\) 为系数的物品进行背包后,后续物品再次进行背包,是不可能影响低于 \(t\) 位的二进制数码的

为了避免状态过多,我们试图采用数位 dp 的方式,一位一位考虑对答案的贡献

注意到若进行到第 \(t\) 位后,若体积 \(V\) 的二进制低 \(t\) 位大于 \(m\) ,即 \(V\bmod 2^t>m\bmod 2^t\) ,则当且仅当 \(V\) 的第 \(t+1\) 位为 \(0\)\(m\) 的第 \(t+1\) 位为 \(1\) 会使得 \(V\bmod 2^{t+1}\leq m\bmod 2^{t+1}\) ;若体积 \(V\) 的二进制低 \(t\) 位小于等于 \(m\) ,即 \(V\bmod 2^t\leq m\bmod 2^t\) ,则当且仅当 \(V\) 的第 \(t+1\) 位为 \(1\)\(m\) 的第 \(t+1\) 位为 \(0\) 会使得 \(V\bmod 2^{t+1}> m\bmod 2^{t+1}\)

因此,我们考虑记 \(dp_{t, i, 0/1}\) 表示进行完 \(2^t\) 为系数的物品的背包后,体积 \(V\) 满足 \(\lfloor{V\over 2^{t+1}}\rfloor=i\) ,且 \(V\bmod 2^{t+1}\) 是否严格大于 \(m\bmod 2^{t+1}\) ,的方案数

故答案是 \(\displaystyle \sum_{i=1}^n a_ix_i\leq m\) ,即是 \(V\leq m\) 的方案数。由于 \(m<2^{60}\) ,故 \(0\leq \lfloor{V\over 2^{59+1}}\rfloor\leq \lfloor{m\over 2^{59+1}}\rfloor<1, V\bmod 2^{59+1}\leq m\bmod 2^{59+1}\) ,因此答案为 \(dp_{59, 0, 0}\)

这种情况下,\(\displaystyle 0\leq t\leq 59, 0\leq i\leq \lfloor{V\over 2^t}\rfloor \leq {1\over 2^t}\sum_{x=0}^t\sum_{i=1}^n 2^x\cdot a_i=\sum_{i=1}^n a_i\cdot {1\over 2^t}\sum_{x=0}^t 2^x\leq 2\sum_{i=1}^n a_i:=2A\)


考虑该 \(dp\) 的转移,首先,我们需要知道系数为 \(2^t\) 的物品的背包结果,它可以用生成函数 \(\displaystyle \prod_{i=1\\(i, t)\not\in \{(b_k, c_k)\}}^n (1+x^{2^t\cdot a_i})\) ,我们记为 \(g_t(x^{2^t})\) ,即 \(\displaystyle g_t(x)=\prod_{i=1\\(i, t)\not\in \{(b_k, c_k)\}}^n (1+x^{a_i})=\prod_{i=1}^n(1+x^{a_i})\cdot [\prod_{(i, t)\in \{(b_k, c_k)\}} (1+x^{a_i})]^{-1}\)

我们可以先用多项式线段树分治或多项式启发式合并,先在 \(O(n\log^2 n)\) 的时间内求出 \(\displaystyle g(x)=\prod_{i=1}^n(1+x^{a_i})\) 。后续求解 \(g_t(x)\) 时,只需要反向跑背包进行删除,复杂度为 \(O(n)\) ;由于限制数量为 \(k\leq 5\times 10^3\) 个,故复杂度 \(O(kn)\) 是可接受的

现在考虑已知 \(dp_{t-1, 0\cdots, 2A,0/1}\) 、已知 \([x^l]g_t(x)\) ,如何合并得出 \(dp_{t, 0\cdots 2A, 0/1}\) 的结果

由于 \(dp_{t-1, i, 0}\) 表示进行完 \(2^{t-1}\) 为系数的物品后,体积 \(V\) 满足 \(\lfloor{V\over 2^t}\rfloor=i\) ,且 \(V\bmod 2^t\leq m\bmod 2^t\) 的方案数;\([x^l]g(x)\) 表示系数为 \(2^t\) 的物品,拼出体积为 \(2^t\cdot l\) 的方案数

因此将两者合并后,新体积 \(\displaystyle V'=V+2^t\cdot l\Rightarrow \lfloor{V'\over 2^{t+1}}\rfloor=\lfloor{V+2^t\cdot l\over 2^{t+1}}\rfloor=\lfloor{i+l\over 2}\rfloor\) ,大小关系 \(\displaystyle b=[V'\bmod 2^{t+1}>m\bmod 2^{t+1}]=[V'_t>m_t]=[\lfloor{V'\over 2^t}\rfloor_0>m_t]=[(i+l)\bmod 2>m_t]\)

于是可以写出一部分的转移式:\(\displaystyle dp_{t, \lfloor{I\over 2}\rfloor, [I\bmod 2>m_t]}=\sum_{i+l=I}dp_{t-1, i, 0}\cdot [x^l]g_t(x)\) ,显然是加法卷积的形式,可以在 \(O(2A\log 2A)\) 的时间内求解

同理,由于 \(dp_{t-1, i, 1}\) 表示进行完 \(2^{t-1}\) 为系数的物品后,体积 \(V\) 满足 \(\lfloor{V\over 2^t}\rfloor=i\) ,且 \(V\bmod 2^t>m\bmod 2^t\) 的方案数

则两者合并后,新体积 \(\displaystyle V'=V+2^t\cdot l\Rightarrow \lfloor{V'\over 2^{t+1}}\rfloor=\lfloor{V+2^t\cdot l\over 2^{t+1}}\rfloor=\lfloor{i+l\over 2}\rfloor\) ,大小关系 \(\displaystyle b=[V'\bmod 2^{t+1}>m\bmod 2^{t+1}]=[\neg (V'_t<m_t)]=[V'_t\geq m_t]=[\lfloor{V'\over 2^t}\rfloor_0\geq m_t]=[(i+l)\bmod 2\geq m_t]\)

于是另一部分转移式为:\(\displaystyle dp_{t, \lfloor{I\over 2}\rfloor, [I\bmod 2\geq m_t]}=\sum_{i+l=I}dp_{t-1, i, 1}\cdot [x^l]g_t(x)\) ,同样是加法卷积,可以在 \(O(2A\log 2A)\) 的时间内求解

初始时,是 \(dp_{0, i, 0/1}\) 表示进行完 \(2^0\) 为系数的物品后,体积 \(\lfloor{V\over 2}\rfloor=i\)\(V\bmod 2\) 是否严格大于 \(m\bmod 2\) ;也是直接由 \([x^l]g_0(x)\) 贡献到 \(dp_{0, \lfloor{l\over 2}\rfloor, l\bmod 2>m\bmod 2}\)


综上,先对 \((1+x^{a_i})\) 进行 \(O(n\log^2 n)\) 的多项式启发式合并或多项式线段树分治;接着对数位进行 DP,先求出 \(g_t(x)\) ,再分别利用 \(dp_{t-1, 0\cdots 2A, 0},dp_{t-1, 0\cdots 2A, 1}\)\(g_t(x)\) 卷积递推 \(dp_{t, 0\cdots 2A, 0/1}\) 的贡献

\(g_t(x)\) 的总复杂度为 \(O(nk)\) ,卷积递推的复杂度为 \(O(\log m\cdot 2\cdot 2A\log 2A)=O(\log m\cdot A\log A)=O(\log m\cdot n\log n)\)

故总复杂度为 \(O(n\log n(\log n+\log m)+nk)\)

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define sz(a) (int)a.size()
#define de(a) cerr << #a << " = " << a << endl
#define dd(a) cerr << #a << " = " << a << " "
#define all(a) a.begin(), a.end()
#define pw(x) (1ll<<(x))
#define lc(x) ((x)<<1)
#define rc(x) ((x)<<1|1)
#define rsz(a, x) (a.resize(x))
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef double db;

const int P=998244353;
inline int kpow(int a, int x, int p=P) { int ans=1; for(;x;x>>=1, a=(ll)a*a%p) if(x&1) ans=(ll)ans*a%p; return ans; }
inline int exgcd(int a, int b, int &x, int &y) {
	static int g;
	return b?(exgcd(b, a%b, y, x), y-=a/b*x, g):(x=1, y=0, g=a);
}
inline int inv(int a, int p=P) {
	static int x, y;
	return (exgcd(a, p, x, y)==1)?((x<0)?x+p:x):(-1);
}
const int LimBit=16;
const int M=1<<LimBit<<1;

namespace Poly{
	const int G=3;
	struct vir {
		int v;
		vir(int v_=0):v(v_>=P?v_-P:v_) {}
		inline vir operator + (const vir &x) const { return vir(v+x.v); }
		inline vir operator - (const vir &x) const { return vir(v+P-x.v); }
		inline vir operator * (const vir &x) const { return vir((ll)v*x.v%P); }
		inline vir operator - () const { return vir(P-v); }
		inline vir operator ! () const { return vir(inv(v)); }
		inline operator int() const { return v; }
	};

	struct poly : public vector<vir> {
		inline friend ostream& operator << (ostream& out, const poly &p) {
			if(!p.empty()) out<<(int)p[0];
			for(int i=1; i<sz(p); ++i) out<<" "<<(int)p[i];
			return out;
		}
	};
	int N, N_, Stk[M], curStk, rev[M];
	vir invN, Inv[M], w[2][M];
	inline void init() {
		N_=-1;
		curStk=0;
		Inv[1]=1;
		for(int i=2; i<M; ++i)
			Inv[i]=-vir(P/i)*Inv[P%i];
	}
	void work(){
		if(N_==N) return ;
		N_=N;
		int d = __builtin_ctz(N);
		vir x(kpow(G, (P-1)/N)), y=!x;
		w[0][0] = w[1][0] = 1;
		for (int i = 1; i < N; ++i) {
			rev[i] = (rev[i>>1] >> 1) | ((i&1) << (d-1));
			w[0][i]=x*w[0][i-1], w[1][i]=y*w[1][i-1];
		}
		invN=!vir(N);
	}
	inline void FFT(vir a[M],int f){
		static auto make = [=](vir w, vir &a, vir &b) { w=w*a; a=b-w; b=b+w; };
		for(int i=0;i<N;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(int i=1;i<N;i<<=1)
			for(int j=0,t=N/(i<<1);j<N;j+=i<<1)
				for(int k=0,l=0;k<i;k++,l+=t)
					make(w[f][l], a[j+k+i], a[j+k]);
		if(f) for(int i=0;i<N;++i) a[i]=a[i]*invN;
	}
	vir p1[M], p0[M];
	
	inline void get_mul(poly &a, poly &b, int na, int nb) {//3*FFT
		for(N=1;N<na+nb-1;N<<=1);
		for(int i=0; i<na; ++i) p1[i]=(int)a[i]; for(int i=na;i<N;++i) p1[i]=0;
		for(int i=0; i<nb; ++i) p0[i]=(int)b[i]; for(int i=nb;i<N;++i) p0[i]=0;
		work(); FFT(p1,0); FFT(p0,0);
		for(int i=0;i<N;++i) p1[i]=p1[i]*p0[i];
		FFT(p1,1);
		rsz(a, na+nb-1); for(int i=0; i<sz(a); ++i) a[i]=p1[i];
	}
	
	poly T[M];
	priority_queue<pii> H;
	inline poly &get_merge(int cnt) {
		while(H.size()) H.pop();
		for(int i=1; i<=cnt; ++i) H.emplace( mp(-sz(T[i]), i) );
		for(int t=H.size(); t>=2; --t) {
			int x=H.top().se; H.pop();
			int y=H.top().se; H.pop();
			get_mul(T[x], T[y], sz(T[x]), sz(T[y]));
			H.emplace( mp(-sz(T[x]), x) );
		}
		return T[H.top().se];
	}
}
using Poly::vir;
using Poly::poly;

const int Lim=8e4, MAXN=Lim+10;
int n, k, a[MAXN];
ll m, mask[MAXN];
poly f, g, h;
inline void divit(poly &f, int a) {
	for(int i=sz(f)-1, j=i-a; j>=0; --i, --j)
		f[j]=f[j]-f[i];
	for(int i=0, j=a, J=sz(f); j<J; ++i, ++j)
		f[i]=f[j];
	rsz(f, sz(f)-a);
}

vir dp[2][MAXN][2];
inline void work() {
	g=f;
	for(int i=1; i<=n; ++i) if(mask[i]&1) divit(g, a[i]);
	for(int i=sz(g)-1; ~i; --i) dp[0][i>>1][(i&1)>(m&1)]=dp[0][i>>1][(i&1)>(m&1)]+g[i];
	
	for(int t=1; t<=59; ++t) {
		auto lst=dp[t+1&1], now=dp[t&1];
		int mt=(m>>t)&1;
		for(int i=0, I=Lim; i<=I; ++i) now[i][0]=now[i][1]=0;
		g=f;
		for(int i=1; i<=n; ++i) if((mask[i]>>t)&1) divit(g, a[i]);
		
		rsz(h, Lim+1);
		for(int i=0; i<=Lim; ++i) h[i]=lst[i][0];
		Poly::get_mul(h, g, sz(h), sz(g));
		for(int i=sz(h)-1; ~i; --i)
			now[i>>1][(i&1)>mt]=now[i>>1][(i&1)>mt]+h[i];
		rsz(h, Lim+1);
		for(int i=0; i<=Lim; ++i) h[i]=lst[i][1];
		Poly::get_mul(h, g, sz(h), sz(g));
		for(int i=sz(h)-1; ~i; --i)
			now[i>>1][(i&1)>=mt]=now[i>>1][(i&1)>=mt]+h[i];
	}
	
	cout<<(int)dp[1][0][0];
}
inline void init() {
	Poly::init();
	cin>>n>>m>>k;
	for(int i=1; i<=n; ++i) {
		cin>>a[i];
		poly &p=Poly::T[i];
		rsz(p, a[i]+1);
		p[0]=p[a[i]]=1;
	}
	f=Poly::get_merge(n);
	
	for(int i=1, x, y; i<=k; ++i)
		cin>>x>>y, mask[x]|=pw(y);
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	init();
	work();
	cout.flush();
	return 0;
}
posted @ 2022-07-20 14:34  JustinRochester  阅读(75)  评论(0编辑  收藏  举报