[ACNOI2022]Patience

280 篇文章 1 订阅

题目

题目背景
哪怕旁边的人说着:“我们是同伴,要将真正的同伴留在心里!” 哪怕旁边的人说着:“尽管这力量很微小,但如果它能左右世界,那我们更应该谨小慎微。” 然而到最后,我也只是作了炮灰。

O U Y E \sf OUYE OUYE 挺身而出,挑起大梁; D D ( X Y X ) \sf DD(XYX) DD(XYX) 一往无前,渴望孤独。那么,我呢?失去了 O U Y E \sf OUYE OUYE 的「卷坷垃」保护的我,发挥过哪怕一点点的作用吗?

题目描述
面前或许有很多数字,但最终有用的寥寥,就像 D D ( X Y X ) \sf DD(XYX) DD(XYX) O U Y E \sf OUYE OUYE 那样。

给出自然数的集合 A = ⋃ i = 1 n [ l i , r i ] A=\bigcup_{i=1}^{n}[l_i,r_i] A=i=1n[li,ri],求有多少种方式将 s s s 表示为 A A A 4 4 4 个不同数字的和。表示方式不同,当且仅当选出的 4 4 4 个数字的集合不同。

数据范围与提示
s ⩽ 8 × 1 0 8 s\leqslant 8\times 10^8 s8×108 n ⩽ 800 n\leqslant 800 n800

思路

说白了是个背包问题,也就是个生成函数问题。写出 F ( x ) = ∑ x l i − x r i + 1 1 − x F(x)=\sum\frac{x^{l_i}-x^{r_i+1}}{1-x} F(x)=1xxlixri+1 是容易的,但是 F ( x ) 4 F(x)^4 F(x)4 并非答案,因为其可能选择了相同的元素。

有一个略去思考过程的结论,见此文最后一节。若不知道,那我们可以从头开始,求出每种个数分配对应的生成函数。例如, G 1 , 2 ( x ) G_{1,2}(x) G1,2(x) 表示某个数选择 1 1 1 次、另一个数选择 2 2 2 次,对应的生成函数。当然,选择次数相同的数字是本质相同的(没有顺序关系)。那么我们可以写出
G 1 , 1 ( x ) = 2 − 1 [ F ( x ) 2 − F ( x 2 ) ] G 1 , 1 , 1 ( x ) = 3 − 1 [ G 1 , 1 ( x ) F ( x ) − G 1 , 2 ( x ) ] G 1 , 1 , 1 , 1 ( x ) = 4 − 1 [ G 1 , 1 , 1 ( x ) F ( x ) − G 1 , 1 , 2 ( x ) ] \begin{aligned} G_{1,1}(x)&=2^{-1}\big[F(x)^2-F(x^2)\big]\\ G_{1,1,1}(x)&=3^{-1}\big[G_{1,1}(x)F(x)-G_{1,2}(x)\big]\\ G_{1,1,1,1}(x)&=4^{-1}\big[G_{1,1,1}(x)F(x)-G_{1,1,2}(x)\big] \end{aligned} G1,1(x)G1,1,1(x)G1,1,1,1(x)=21[F(x)2F(x2)]=31[G1,1(x)F(x)G1,2(x)]=41[G1,1,1(x)F(x)G1,1,2(x)]

形式是相仿的,应该容易理解, G ⋯   , 2 ( x ) G_{\cdots,2}(x) G,2(x) 就是出现重复的情况。除以 2 / 3 / 4 2/3/4 2/3/4 是取消选择次数相同的数字之间的顺序关系。

类似地,我们可以把需要用到的值都补齐
G 1 , 2 ( x ) = F ( x 2 ) F ( x ) − F ( x 3 ) G 1 , 1 , 2 ( x ) = G 1 , 1 ( x ) F ( x 2 ) − G 1 , 3 ( x ) G 1 , 3 ( x ) = F ( x ) F ( x 3 ) − F ( x 4 ) \begin{aligned} G_{1,2}(x)&=F(x^2)F(x)-F(x^3)\\ G_{1,1,2}(x)&=G_{1,1}(x)F(x^2)-G_{1,3}(x)\\ G_{1,3}(x)&=F(x)F(x^3)-F(x^4) \end{aligned} G1,2(x)G1,1,2(x)G1,3(x)=F(x2)F(x)F(x3)=G1,1(x)F(x2)G1,3(x)=F(x)F(x3)F(x4)

然后按照定义,层层展开。过程较长且无技术含量,这里略去。
24 G 1 , 1 , 1 , 1 ( x ) = F ( x ) 4 − 6 F ( x 2 ) F ( x ) 2 + 3 F ( x 2 ) 2 + 8 F ( x 3 ) F ( x ) − 6 F ( x 4 ) 24G_{1,1,1,1}(x)=F(x)^4-6F(x^2)F(x)^2+3F(x^2)^2+8F(x^3)F(x)-6F(x^4) 24G1,1,1,1(x)=F(x)46F(x2)F(x)2+3F(x2)2+8F(x3)F(x)6F(x4)

答案就是 [ x s ] G 1 , 1 , 1 , 1 ( x ) [x^s]G_{1,1,1,1}(x) [xs]G1,1,1,1(x) 。对于 F ( x 2 ) 2 F(x^2)^2 F(x2)2 F ( x 3 ) F ( x ) F(x^3)F(x) F(x3)F(x) F ( x 4 ) F(x^4) F(x4),它们的分子都不超过 O ( n 2 ) \mathcal O(n^2) O(n2) 项,可以暴力卷积。事实上,直接按照定义去计算,复杂度就能做到 O ( n 2 ) \mathcal O(n^2) O(n2) 了;只是没必要。把分子乘出来,枚举每一项,就知道它除以分母后对 x s x^s xs 的贡献。

可惜的是,指数过于离散,需要最后再排序。所以逃不过 O ( n 2 log ⁡ n ) \mathcal O(n^2\log n) O(n2logn) 了。

F ( x ) 4 F(x)^4 F(x)4 呢?可以背包合并,即 Meet-In-Middle \text{Meet-In-Middle} Meet-In-Middle 。求出 F ( x ) 2 F(x)^2 F(x)2 对应的分子 H ( x ) H(x) H(x),接下来只需要计算
[ x s ] H ( x ) 2 ( 1 − x ) − 4 = ∑ κ i + κ j ⩽ s c κ i c κ j ( s − κ i − κ j + 3 3 ) [x^s]H(x)^2(1-x)^{-4}=\sum_{\kappa_i+\kappa_j\leqslant s}c_{\kappa_i}c_{\kappa_j}{s-\kappa_i-\kappa_j+3\choose 3 } [xs]H(x)2(1x)4=κi+κjscκicκj(3sκiκj+3)

其中 c κ i = [ x κ i ] H ( x ) c_{\kappa_i}=[x^{\kappa_i}]H(x) cκi=[xκi]H(x) 。由于指数并不形成区间,故用 κ i \kappa_i κi 表示。

κ i \kappa_i κi 是可枚举的,怎样快速求出该式对 j j j 的求和呢?说白了,就是要把那个组合数写成关于 κ i \kappa_i κi 的式子。可以直接按照定义展开成下降幂;也可以稍微高级一点,用二项式卷积,展开为
= ∑ κ i + κ j ⩽ s c κ i ∑ p = 0 3 ( − κ i 3 − p ) ( s − κ j + 3 p ) c κ j =\sum_{\kappa_i+\kappa_j\leqslant s}c_{\kappa_i}\sum_{p=0}^{3}{-\kappa_i\choose 3-p}{s-\kappa_j+3\choose p}c_{\kappa_j} =κi+κjscκip=03(3pκi)(psκj+3)cκj

于是我们只需要枚举 i , p i,p i,p,然后对 κ j \kappa_j κj 的前缀求和。时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

类似地,研究 F ( x 2 ) F ( x ) 2 F(x^2)F(x)^2 F(x2)F(x)2 怎么求。还是求出两个分子 H 1 ( x ) , H 2 ( x ) H_1(x),H_2(x) H1(x),H2(x),则只需求出
[ x s ] H 1 ( x ) H 2 ( x ) ( 1 − x 2 ) ( 1 − x ) 2 [x^s]{H_1(x)H_2(x)\over (1-x^2)(1-x)^2} [xs](1x2)(1x)2H1(x)H2(x)

这个 ( 1 − x 2 ) (1-x^2) (1x2) 有点碍事。但是别怕,我们还是按照定义把 ( 1 − x 2 ) ( 1 − x ) 2 (1-x^2)(1-x)^2 (1x2)(1x)2 的倒数求出来:
∑ i = 0 + ∞ x i ∑ j = 0 ⌊ i 2 ⌋ ( i − 2 j + 1 ) \sum_{i=0}^{+\infty}x^i\sum_{j=0}^{\lfloor{i\over 2}\rfloor}(i-2j+1) i=0+xij=02i(i2j+1)

显然右侧求和结果是关于 ⌊ i 2 ⌋ \lfloor{i\over 2}\rfloor 2i i i i 的多项式。当 2 ∣ i 2\mid i 2i 时,其等于 ( i + 2 2 ) 2 (\frac{i+2}{2})^2 (2i+2)2,当 2 ∤ i 2\nmid i 2i 时,其等于 ( i + 1 ) ( i + 3 ) 4 \frac{(i+1)(i+3)}{4} 4(i+1)(i+3) 。无论如何,是个关于 i i i 的二次式,记为 ω ( i ) \omega(i) ω(i)

按照 i i i 的奇偶性分开计算,大抵是要计算这样的式子:
∑ κ i + ν j ⩽ s [ κ i + ν j ≡ λ ] c κ i d ν j ⋅ ω ( s − κ i − ν j ) \sum_{\kappa_i+\nu_j\leqslant s}[\kappa_i+\nu_j\equiv\lambda]c_{\kappa_i}d_{\nu_j}\cdot \omega(s{\rm-}\kappa_i{\rm-}\nu_j) κi+νjs[κi+νjλ]cκidνjω(sκiνj)

其中 λ ∈ { 0 , 1 } \lambda\in\{0,1\} λ{0,1} 用来固定 ω \omega ω 自变量奇偶性, c κ i , d ν j c_{\kappa_i},d_{\nu_j} cκi,dνj 仍表示离散的系数。同理 κ i \kappa_i κi 是可枚举的,同理需要将 ω ( s − κ i − ν j ) \omega(s{\rm-}\kappa_i{\rm-}\nu_j) ω(sκiνj) 写成关于 κ i \kappa_i κi 的式子。这次最好还是直接代入,毕竟 ω \omega ω 不过是个二次式。而且要按照 ν j \nu_j νj 的奇偶性分类,分别存储两种 ω ( i ) \omega(i) ω(i)

总时间复杂度 O ( n 2 log ⁡ n ) \mathcal O(n^2\log n) O(n2logn) 。五个部分真的是独立的,非常讨厌。

代码

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG LONG for loneliness)
#include <cctype> // DDG yydDOG & ZXY yydBUS !!!
#include <vector>
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MOD = 998244353;
inline void modAddUp(int &x, const int &y){
	if((x += y) >= MOD) x -= MOD;
}
typedef std::pair<int,int> PII;
typedef std::vector<PII> vecset;
void unique(vecset &v){
	if(v.empty()) return ;
	vecset::iterator i = v.begin();
	for(vecset::iterator j=i+1; j!=v.end(); ++j)
		if(j->first == i->first) // merge
			modAddUp(i->second,j->second);
		else ++ i, *i = *j; // new element
	++ i; v.resize(i-v.begin());
}
vecset convolution(const vecset &va, const vecset &vb){
	vecset res; res.clear();
	for(const PII &a : va) for(const PII &b : vb)
		res.push_back(PII{a.first+b.first,int(
			llong(a.second)*b.second%MOD)});
	std::sort(res.begin(),res.end());
	unique(res); return res;
}
/// @brief change indepent variable to @p k times x 
vecset mulx(const vecset &v, const int &k){
	vecset res = v; // copy
	for(PII &a : res) a.first *= k;
	return res;
}

struct function{
	int coe[3]; ///< quadratic
	function operator + (const function &b) const {
		function c; memcpy(c.coe,coe,sizeof(coe));
		rep0(i,0,3) modAddUp(c.coe[i],b.coe[i]);
		return c;
	}
	function operator * (const int &x) const {
		function c;
		rep0(i,0,3) c.coe[i] = int(llong(x)*coe[i]%MOD);
		return c;
	}
	void clear(){ memset(coe,0,sizeof(coe)); }
	int at(const int &x) const {
		return int(((llong(coe[2])*x
			+coe[1])%MOD*x+coe[0])%MOD);
	}
};
const int inv2 = (MOD+1)>>1, inv4 = 748683265;
/// @return the function when the argument is even number ( @p x0 - x )
function omega0(int x0){
	function c; c.coe[2] = inv4, x0 += 2;
	x0 = int(llong(x0)*inv2%MOD), c.coe[1] = MOD-x0;
	*c.coe = int(llong(x0)*x0%MOD); return c;
}
function omega1(int x0){
	function c; c.coe[2] = inv4, x0 += 2;
	*c.coe = int((llong(x0)*x0-1)%MOD*inv4%MOD);
	c.coe[1] = int(llong(MOD-x0)*inv2%MOD); return c;
}

vecset fx; ///< overall OGF
function func[2][2]; ///< parity of exponent, type of function
const int inv3 = (MOD+1)/3;
int main(){
	int n = readint(), s = readint();
	int ans = 0; // calc -6 * F(x^4) by definition
	for(int i=0,l,r,lst=-2; i!=n; ++i){
		l = readint(), r = readint();
		if(l == lst+1) // connect
			fx.back().first = r+1;
		else{ // new range
			fx.push_back(PII{l,1}), lst = r;
			fx.push_back(PII{r+1,MOD-1});
		}
		if(!(s&3) && l <= (s>>2) && (s>>2) <= r)
			ans = MOD-6; // easy calculate
	}
	{ // calc 8 * F(x^3) * F(x)
		vecset &&fx3 = mulx(fx,3);
		vecset &&got = convolution(fx3,fx);
		for(const PII &v : got){
			if(v.first > s) break;
			llong d = (s-v.first)/3+1;
			ans = int((ans+(d*v.second<<3))%MOD);
		}
	}
	vecset &&fx2 = mulx(fx,2); ///< F(x^2)
	if(!(s&1)){ // calc 3 * F(x^2) * F(x^2)
		vecset &&got = convolution(fx2,fx2);
		for(const PII &v : got){
			if(v.first > s) break;
			llong d = ((s-v.first)>>1)+1;
			ans = int((ans+3*d*v.second)%MOD);
		}
	}
	vecset &&fx_2 = convolution(fx,fx); ///< F(x)^2
	{ // calc -6 * F(x^2) * F(x)^2
		rep0(p,0,2) rep0(q,0,2) func[p][q].clear();
		vecset::iterator i = fx2.end(), j = fx_2.begin();
		while(i != fx2.begin()){
			if((--i)->first > s) continue;
			for(; j!=fx_2.end()&&j->first+i->first<=s; ++j)
				rep0(q,0,2){ // type of function
					function &fc = func[j->first&1][q];
					if(q) fc = fc+omega1(s-j->first)*j->second;
					else fc = fc+omega0(s-j->first)*j->second;
				}
			rep0(p,0,2) ans = int((ans+llong(MOD-6)*i->second
				%MOD*func[p][(s^p^i->first)&1].at(i->first))%MOD);
		}
	}
	{ // calc F(x)^2 * F(x)^2
		const int inv[] = {1,inv2,inv3,0};
		int xjx[4] = {0,0,0,0}; // coefficient
		vecset::iterator i = fx_2.end(), j = fx_2.begin();
		while(i != fx_2.begin()){
			if((--i)->first > s) continue;
			for(; j!=fx_2.end()&&j->first+i->first<=s; ++j)
				for(int p=0,v=j->second; p<=3; ++p){
					modAddUp(xjx[p],v), v = int(inv[p]*
						llong(s-j->first+3-p)%MOD*v%MOD);
				}
			for(int p=0,v=i->second; p<=3; ++p){
				ans = int((ans+llong(xjx[3-p])*v)%MOD);
				v = int(llong(MOD-i->first-p)*v%MOD*inv[p]%MOD);
			}
		}
	}
	ans = int(llong(ans)*inv4
		%MOD*inv3%MOD*inv2%MOD);
	printf("%d\n",ans); // div 24
	return 0;
}

后记

当我好不容易做出这道题时,我才发现 O U Y E \sf OUYE OUYE 已经把今天的三道题都做完了。

Q Q \rm QQ QQ 群里一片摆烂之声。不是别的原因,只是单纯地想知道:我为什么要存在于此。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值