首页 > 其他分享 >CF280E - Sequence Transformation

CF280E - Sequence Transformation

时间:2023-05-24 21:57:58浏览次数:36  
标签:ld le Sequence tr mov 我们 CF280E Transformation dp

给定一个不降整数序列 \(1\le x_1\le x_2\le \cdots\le x_n\le q\),请构造一个实数序列 \(y\) 满足 \(y_i\in [1,q]\),\(y_i-y_{i-1}\in[a,b]\),且最小化 \(\sum (y_i-x_i)^2\),保证有解。

利用凸函数性质维护导数

我们设 \(dp_i(u)\) 表示对于所有的合法的 \(u\),\(y_i=u\) 时 \(\sum_{j\le i}(y_j-x_j)^2\) 的最小值。

然后有转移式,设 \(dp_i(u)\) 的最小值在 \(v\) 处取到,那么 \(dp_{i+1}(u)\) 就应该是 \(dp_{i+1}(u)=\min_{d\in[a,b]} dp_{i}(u-d)+(u-x_j)^2\)。

我们发现 \(dp_i(u)\) 是有点棘手的,但是我们转而研究 \(dp_i'(u)\)。首先,我们发现 \(dp_i'(u)\) 是单调的,也就是 \(dp_i(u)\) 是凸的。考虑数学归纳法,在 \(dp_1(u)\),因为只是一个开口向上的二次函数,所以其导数必然是单调增的。

然后如果 \(dp_{i-1}(u)\) 满足条件,就很好了,因为凸函数的转移是显然的。设 \(dp_{i-1}(u)\) 的最小值取在 \(k_i\)(注意因为有定义域限制,所以此处导数不一定为 \(0\))

所能转移到的函数段单调下降,

\[dp_{i}(u)=dp_{i-1}(u-a)+(u-x_i)^2(u\lt k_i+a) \]

所能转移到的函数段包含顶点,

\[dp_{i}(u)=dp_{i-1}(k_i)+(u-x_i)^2(k_i+a\le u\le k_i+b) \]

所能转移到的函数段单调上升,

\[dp_{i}(u)=dp_{i-1}(u-b)+(u-x_i)^2(u\gt k_i+b) \]

这是很好证明的,在凸函数上这些都是显然的。然后我们就发现,如果是导数的话:

\[dp_{i}'(u)=dp_{i-1}'(u-a)+2u-2x_i(u\lt k_i+a) \]

\[dp_{i}'(u)=2u-2x_i(k_i+a\le u\le k_i+b) \]

\[dp_{i}'(u)=dp_{i-1}'(u-b)+2u-2x_i(u\gt k_i+b) \]

这就很好了!因为我们发现,新的函数就是把原来的函数从中间断开,然后左边往右平移 \(a\),右边往右平移 \(b\),中间用 \(0\) 填满。这是单调不降的。然后整体加上单调上升的 \(2u-2x_i\),我们发现 \(dp_i(u)\) 也是单调上升的。我们就证明了任意的 \(dp_i(u)\) 都是单调上升的。

然后我们发现,我们每次只是平移,然后加上一次函数,所以导函数也一直是一次函数,这就很好分段维护了。考虑分段维护当前所有的 \(dp_i'(x)\),每次只需要找到零点断开,左右分别平移,中间安排 \(0\),然后整体加上 \(2u-2x_i\)。每次最多增加两个段,所以最终的段个数不会超过 \(2n\),复杂度 \(O(n^2)\),是可以通过的。

讲一下卡了我很久的一个问题,就是我们的函数可能不是连续的。我一开始认为它一定是连续的,所以在发现函数不连续且没有 \(0\) 点的时候我就一直在找转移的问题,但实际上,我们的原函数本来就不是光滑的,插入一次函数 \(0u+0\) 之后导函数也不是连续的。但是我们的 \(0\) 点可以正常找,因为 \(0\) 点的定义并不是导数为 \(0\) 的点,而是原函数取到最小值的点。这时候如果相邻两段导函数一个右端点取值是负的,一个左端点取值是正的,就可以直接用它们交接的地方做 \(0\) 点,端点同理。

最后我们以 \(y_n=k_n\) 为基础倒推 \(y\),因为 \(k_i\) 是使得当前取值最小的位置,而 \(dp_i(u)\) 还是个凸函数,所以很显然,尽可能的让 \(y_{i-1}\) 在不违反 \(y_i\) 条件的情况下最接近 \(k_{i-1}\) 即可。然后直接用得到的 \(y\) 计算答案。

const int N=6000;
const ld limit=0.000000001;
ll n,x[N+5],q,a,b;
ld y[N+5];
struct slope{
	ld k,b,l,r;
	slope(ld K,ld B,ld L,ld R){
		k=K,b=B,l=L,r=R;
	}
	slope(){
		k=0,b=0,l=0,r=0;
	}
	ld zerop(){
		return -b/k;
	}
};
slope dp[3*N+5],f[3*N+5];
ld k[N+5];
int m,cnt;
int main(){
	scanf("%lld%lld%lld%lld",&n,&q,&a,&b);
	rep(i,1,n)scanf("%lld",&x[i]);
	dp[m=1]=slope(2,-2*x[1],1,q);
	rep(i,2,n){
		cnt=0;
		k[i]=dp[1].l;
		rep(j,1,m){
			ld c=dp[j].zerop();
			if(dp[j].l<c+limit && c<dp[j].r+limit)k[i]=c;
			if(j!=1&&dp[j-1].k*dp[j].l+dp[j-1].b+limit<0){
				if(dp[j].k*dp[j].l+dp[j].b>limit)k[i]=dp[j].l;
			}
		}
		if(k[i]>q+limit)k[i]=q;
		rep(j,1,m){
			if(dp[j].r<k[i]+limit){//r<=k
				f[++cnt]=slope(dp[j].k,dp[j].b-a*dp[j].k,dp[j].l+a,dp[j].r+a);
			}else if(dp[j].l>k[i]+limit){//l>k
				f[++cnt]=slope(dp[j].k,dp[j].b-b*dp[j].k,dp[j].l+b,dp[j].r+b);
			}else{//l<=k<r
				if(dp[j].l+limit<k[i]){//l<k
					f[++cnt]=slope(dp[j].k,dp[j].b-a*dp[j].k,dp[j].l+a,k[i]+a);
				}
				f[++cnt]=slope(0,0,k[i]+a,k[i]+b);
				f[++cnt]=slope(dp[j].k,dp[j].b-b*dp[j].k,k[i]+b,dp[j].r+b);
			}
		}
		m=cnt;
		rep(j,1,m){
			dp[j]=f[j];
			dp[j].k=dp[j].k+2;
			dp[j].b=dp[j].b-2*x[i];
		}
	}
	ld ans=dp[1].l;
	rep(j,1,m){
		ld c=dp[j].zerop();
		if(dp[j].l<c+limit && c<dp[j].r+limit)ans=c;
		if(j!=1&&dp[j-1].k*dp[j].l+dp[j-1].b+limit<0)
			if(dp[j].k*dp[j].l+dp[j].b>limit)ans=dp[j].l;
	}
	if(ans>q+limit)ans=q;
	per(i,1,n){
		y[i]=ans;
		if(ans-b+limit>k[i])ans=ans-b;
		else if(ans-a<k[i]+limit)ans=ans-a;
		else ans=k[i];
	}
	ld sum=0;
	rep(i,1,n){
		sum=sum+(x[i]-y[i])*(x[i]-y[i]);
		printf("%.8Lf ",y[i]);
	}
	printf("\n%.8Lf\n",sum);
	return 0;
}
//Nyn-siyn-hert

二次函数规划

金老师讲的做法,妙妙。

这个算法的核心是这样的。假设当 \(y_i=dp_i\) 时存在最优化 \(\sum_{j\le i}(x_j-y_j)^2\) 的一个方案。我们通过调整原先的答案依次求出 \(dp_i\),然后同样选取结果倒推。

我们需要实现一个功能,一个函数 ld solve(int i,ld l,ld r,ld A,ld B),表示返回一个 \(v\),使得对于前 \(i\) 个位置,存在一个解 \(\{y\}\) 满足 \(y_i=v\) 并且 \(\sum_{j<i}(y_j-x_j)^2+Av^2+Bv\) 在 \(v\in[l,r]\) 的限制条件下 被该方案最优化。

我们发现,想要求出 \(dp_i\),其实就是做一个 \(l=1,r=q\) 的规划,这个规划要最优化的是 \(\sum_{j\le i}(y_j-x_j)^2\),忽略常数项(最优解的取值和常数项无关,即二次函数顶点横坐标和常数无关)也就是 \(\sum_{j<i}(y_j-x_j)^2+y_i^2-2x_iy_i\)。即 \(A=1,B=-2x_i\)。

然后考虑如何求取规划。

首先证明一个引理:

  • 假设存在一个前 \(i\) 位的子问题的最优解 \(\{y\}\),并且 \(y_i+b<cur\)(\(cur\) 是 \(Ay_{i+1}^2+By_{i}\) 的最优解),那么一定存在一个前 \(i+1\) 位的子问题的最优解 \(\{z\}\) 满足 \(z_i+b=z_{i+1}\)。

这个结论看起来是显然的,不过我们还是证一下。(金老师上课没讲,所以这个做法是我自己搞的,需要上一个做法的结论。不知道金老师的证法需不需要)

首先考虑最优解的 \(y_{i+1}\),如果有多个解,我们选取 \(y_{i+1}\) 最大的一个,都相同就选取 \(y_{i}\) 最小的一个。

然后假设 \(y_{i}>y_{i+1}-b\),那么因为第一个做法中转移函数的凸性,所以 \(y_i=y_{i+1}-b\) 一定不会更劣(其实是严格更优)。但是这就和当前解是 \(y_i\) 最小的最优解矛盾,所以得证。

同理,又有

  • 假设存在一个前 \(i\) 位的子问题的最优解 \(\{y\}\),\(y_i+a>cur\)(\(cur\) 是 \(Ay_{i+1}^2+By_{i}\) 的最优解),那么一定存在一个前 \(i+1\) 位的子问题的最优解 \(\{z\}\) 满足 \(z_i+a=z_{i+1}\)。

这个证明过程和上面是一样的,就不证了。

那么,现在我们尝试规划 \(y_i\),先把前面的东西当作常数,单独找到 \(Ay_i^2+By_i\) 的最优解 \(y_i=cur\)。这是二次函数顶点,注意有边界。

然后,如果 \(cur\in[dp_{i-1}+a,dp_{i-1}+b]\),那么我们就找到当前规划的最优解(前半段和后半段同时最优化),直接返回 \(cur\)。

如果 \(cur>dp_{i-1}+b\),由引理可知一定存在一个解使得 \(y_{i-1}+b=y_{i}\),那么就直接令 \(y_i=y_{i-1}+b\),这样 \(\sum_{j<i}(y_j-x_j)^2+Ay_i^2+By_i\) 也就变成了 \(\sum_{j<i}(y_j-x_j)^2+A(y_{i-1}+b)^2+B(y_{i-1}+b)\),忽略常数项即为 \(\sum_{j<i-1}(y_j-x_j)^2+(A+1)y_{i-1}^2+(B-2x_{i-1}+2bA)y_{i-1}\)。同时为了满足当前规划条件,还要 \(y_{i-1}\in[l-b,r-b]\)。这就是一个 \(i'=i-1\) 的子规划问题。递归求解直至和上一位不冲突或没有上一位即可。

每次最多倒退 \(O(n)\) 次,所以复杂度是 \(O(n^2)\)。

const ld eps=1e-8;
int n,x[6005],q,a,b;
ld dp[6005],y[6005],ans;
inline bool lt(ld x,ld y){
	return x+eps<y;
}
inline bool gt(ld x,ld y){
	return x>y+eps;
}
inline bool le(ld x,ld y){
	return x<y+eps;
}
inline ld solve(int i,ld l,ld r,ld A,ld B){
	if(lt(l,1))l=1;
	if(lt(q,r))r=q;
	ld cyr=-B/(2*A);
	if(lt(r,cyr))cyr=r;
	if(lt(cyr,l))cyr=l;
	if(i==1||(le(dp[i-1]+a,cyr)&&le(cyr,dp[i-1]+b))){
		return cyr;
	}else if(gt(dp[i-1]+a,cyr)){
		return solve(i-1,l-a,r-a,A+1,B-2*x[i-1]+2*a*A)+a;
	}else{
		return solve(i-1,l-b,r-b,A+1,B-2*x[i-1]+2*b*A)+b;
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>q>>a>>b;
	rp(i,n)cin>>x[i];
	dp[1]=x[1];
	rep(i,2,n){
		if(le(dp[i-1]+a,x[i])&&le(x[i],dp[i-1]+b)){
			dp[i]=x[i];
		}else if(gt(dp[i-1]+a,x[i])){
			dp[i]=solve(i,1,q,1,-2*x[i]);
		}else{
			dp[i]=solve(i,1,q,1,-2*x[i]);
		}
	}ld res=dp[n];
	cout<<fixed<<setprecision(8);
	per(i,1,n){
		y[i]=res;
		ans+=(res-x[i])*(res-x[i]);
		if(gt(dp[i-1]+a,res))res=res-a;
		else if(gt(res,dp[i-1]+b))res=res-b;
		else res=dp[i-1];
	}
	rep(i,1,n)cout<<y[i]<<" ";
	cout<<endl;
	cout<<ans<<endl;
	return 0;
}
//Nyn-siyn-hert

\(\text{FHQTreap}\) 优化导数维护

我们考虑使用类似 \(\text{FHQTreap}\) 的数据结构维护导数。数据结构是为算法服务的,我们可以调整数据结构从而更加契合我们的算法。

首先看到 split,这个在无旋平衡树很多时候是核心操作,但是这里我们只需要插入,还不是按照下标插入。而我们发现我们还有一个功能是找零点,那么为什么不把这两个功能合并起来呢?

具体而言就是,我们用两个函数 splitlsplitr,一个把右端点取值大于等于 \(0\) 的 \(split\) 到右边,这样先 splitr 一次得到 \(l\) 和 \(r\),右边的子树右端点取值就都大于等于 \(0\)。然后 splitl 把左端点取值大于等于 \(0\) 的划分到 右边,这样我们再把 \(r\) \(split\) 成 \(tmp\) 和 \(r\),我们会惊奇的发现:

如果 \(tmp\) 不为零,它一定是跨越正负的。也就是零点一定在 \(tmp\) 里面。

如果 \(tmp\) 为 \(0\),那么意味着没有 \(0\) 点,那么 \(0\) 点就在左树和右树的交界点,这正好是右子树的最左点的左边界。我们就很好的把“找到目标位置”和“插入”直接合并起来了。

然后考虑我们需要进行的操作,一个是子树整体平移,一个是子树整体加一次函数。我们可以设计三个 \(tag\),\(addk,addb,move\)。\(move\) 是把定义域向右平移 \(move\),然后给 \(b\) 减去 \(kmove\)。\(add\) 就是简单的加法。这样就不可避免的涉及到下传多 \(tag\) 的顺序问题。这里,我们钦定先执行 \(move\),再执行加法。

然后考虑合并 \(tag\),原先有的 \(tag\) 是 \(add\) 和 \(mov\),新增的是 \(add'\) 和 \(mov'\)。本来的顺序应该是 \(mov\rightarrow add\rightarrow mov'\rightarrow add'\)。

但是合并 \(tag\) 使得顺序变成了 \(mov\rightarrow mov'\rightarrow add\rightarrow add'\)。这样就相当于交换了 \(mov'\) 和 \(add\) 的位置。\(mov'\) 对 \(add\) 是没有贡献的,但是 \(add\) 对 \(mov'\) 是有贡献的。

具体而言,本来的 \(b\) 应该是 \(b+addb-mov'(k+addk)+addb'\),但是现在却是 \(b+addb-mov'k+addb'\),这就少减了一个 \(mov'addk\),不过很好的是 \(add\) 对 \(mov\) 都是没有贡献的。所以直接把这个减在 \(addb\) 上就能消除交换顺序造成的影响了。

而我们只需要在 split 之后给 \(l\) 和 \(r\) 分别打上右移 \(a\) 和 \(b\) 的 \(tag\)。

然后考虑 merge 的问题。我们发现,我们的树还有一个好处,就是我们不需要上传什么东西。我们不需要维护区间信息。那么在没有 pushup 的情况下,我们的 merge 就会好些很多。

最后是插入新段。对于 \(tmp\) 为 \(0\) 的情况,可以直接把 \(tmp\) 作为新点插入。否则,我们可以开两个新点作为 \(tmp\) 的左右儿子,一个记录 \(tmp\) 零点左边的部分右移 \(a\) 位的结果,另一个记录右边部分右移 \(b\) 位的结果。然后就可以直接 merge,避免了开一堆新的单点子树然后套好多层 merge 的情况。

我们直接 split 出最终的零点,倒推答案。

const ld eps=1e-8;
int n,X[6005],q,a,b;
ld dp[6005],y[6005],ans;
inline bool lt(ld x,ld y){
	return x+eps<y;
}
mt19937 rng(time(0));
struct node{
	ld k,b,l,r,addk,addb;
	int ls,rs,mov;
	node(){}
	node(ld _k,ld _b,ld _l,ld _r){
		mov=0,addk=0,addb=0,ls=0,rs=0;
		k=_k,b=_b,l=_l,r=_r;
	}
}tr[114514];
int cnt=0,root;
#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
inline void addtg(int x,ld k,ld b,int m){
	if(!x)return;
	tr[x].addb+=-m*tr[x].addk+b;
	tr[x].addk+=k;
	tr[x].mov+=m;
}
inline void pushdown(int x){
	if(!x)return;
	tr[x].l+=tr[x].mov;
	tr[x].r+=tr[x].mov;
	tr[x].b-=tr[x].mov*tr[x].k;
	tr[x].k+=tr[x].addk;
	tr[x].b+=tr[x].addb;
	addtg(ls(x),tr[x].addk,tr[x].addb,tr[x].mov);
	addtg(rs(x),tr[x].addk,tr[x].addb,tr[x].mov);
	tr[x].addk=0,tr[x].addb=0,tr[x].mov=0;
}
inline void splitl(int x,int &l,int &r){
	if(!x)return (void)(l=r=0);
	pushdown(x);
	ld val=tr[x].k*tr[x].l+tr[x].b;
	if(le(0,val)){
		r=x,splitl(ls(x),l,ls(x));
	}else l=x,splitl(rs(x),rs(x),r);
}
inline void splitr(int x,int &l,int &r){
	if(!x)return (void)(l=r=0);
	pushdown(x);
	ld val=tr[x].k*tr[x].r+tr[x].b;
	if(lt(0,val)){
		r=x,splitr(ls(x),l,ls(x));
	}else l=x,splitr(rs(x),rs(x),r);
}
inline int merge(int l,int r){
	pushdown(l);
	pushdown(r);
	if(!l)return r;
	if(!r)return l;
	int key=rng()%2;
	if(key){
		tr[r].ls=merge(l,tr[r].ls);
		return r;
	}else {
		tr[l].rs=merge(tr[l].rs,r);
		return l;
	}
}
inline int find_first(int x){
	return tr[x].ls?find_first(tr[x].ls):x;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>q>>a>>b;
	rp(i,n)cin>>X[i];
	cnt++,root=1,tr[0].l=q;
	tr[1]=node(2,-2*X[1],1,q);
	rep(i,2,n){
		int l,x,r,R;
		splitr(root,l,r),splitl(r,x,r);
		addtg(l,0,0,a),addtg(r,0,0,b);
		R=find_first(r);
		if(!x){
			x=++cnt,dp[i-1]=tr[R].l;
			tr[x]=node(0,0,tr[R].l+a,tr[R].l+b);
			root=merge(merge(l,x),r);
		}else{
			ld cyr=-tr[x].b/tr[x].k;
			dp[i-1]=cyr;
			if(lt(tr[x].l,cyr)){
				tr[++cnt]=node(tr[x].k,tr[x].b-a*tr[x].k,tr[x].l+a,cyr+a);
				tr[x].ls=cnt;
			}
			if(lt(cyr,tr[x].r)){
				tr[++cnt]=node(tr[x].k,tr[x].b-b*tr[x].k,cyr+b,tr[x].r+b);
				tr[x].rs=cnt;
			}tr[x].k=0,tr[x].b=0,tr[x].l=cyr+a,tr[x].r=cyr+b;
			root=merge(merge(l,x),r);
		}
		addtg(root,2,-2*X[i],0);
	}
	cout<<fixed<<setprecision(8);
	int l,x,r,R;
	splitr(root,l,r);
	splitl(r,x,r);
	R=find_first(r);
	ld res=(x?(-tr[x].b/tr[x].k):tr[R].l);
	if(lt(q,res))res=q;
	per(i,1,n){
		y[i]=res;
		ans+=(res-X[i])*(res-X[i]);
		if(gt(dp[i-1]+a,res))res=res-a;
		else if(gt(res,dp[i-1]+b))res=res-b;
		else res=dp[i-1];
	}
	rep(i,1,n)cout<<y[i]<<" ";
	cout<<endl;
	cout<<ans<<endl;
	return 0;
}
//Nyn-siyn-hert

两个做法之间的共同点

实际上,我们发现,这道题最关键的地方就是“对前 \(i\) 个子问题分别求解”。我们每次先把前缀 \(i\) 当成独立的子问题求解,然后推出 \(i+1\)。

三个做法中,这个东西都至关重要。

在第一个和第三个做法中,这个点是原函数的顶点,导函数的零点,有了它才能决定新函数在哪里断开。

在第二个做法中,这个点是预处理的前缀问题的最优解。当一个前缀问题中,前缀的前缀前缀的最优解和前缀的后缀的最优规划互不冲突,就找到了当前子问题的解。然后就可以往后推了。

这是数学归纳思想,或者说递推思想在 \(\text{OI}\) 中重要的体现。

这道题我学到了什么

其实,最主要的是第三个做法,搞清楚了多个互相影响的 \(tag\) 同时下传的时候如何分析它们之间的影响并将其消除。在其他地方可谓用途很大。

然后,就是用分段函数 dp 的方法。不知道是不是我做题太少,没有遇到其他这样的题。

最后,是子问题规划的思想。但是在现在的我看来这个做法就像一个纸飞机,只有别人当面给我折叠一遍,我才能理解这个方法。否则这种巧妙的做法是我完全无法想到,下次遇到类似题目也难以再一次做出的。所以它对现在的我来说反而价值不大,不知道未来的自己能不能真正掌握这种思想。

标签:ld,le,Sequence,tr,mov,我们,CF280E,Transformation,dp
From: https://www.cnblogs.com/jucason-xu/p/17429413.html

相关文章

  • abc271_e Subsequence Path 题解
    SubsequencePath题意有\(n\)个城市和\(m\)条有向道路,编号从\(1\)开始,第\(i\)条道路从\(a_i\)到\(b_i\),长度为\(c_i\)。给定一个长度为\(k\)的序列\(e\),我们定义从\(1\)到\(n\)的一条路径是优秀的当且仅当:经过的边的编号按顺序构成\(e\)的一个子序列。......
  • Sequence Trigger
    SQL>SQL>createsequencesq12startwith13incrementby14minvalue15maxvalue99999996nocycle7nocache8noorder;SequencecreatedSQL>SQL>createorreplacetriggerpn_trigger2beforeinsertonusers......
  • subsequence1 (牛客多校) (2个串比大小, DP, 组合数)
    题面大意:给定2个字符串,问有多少个子字符串S,是大于t的 思路数据范围很小,因此考虑n^2做法分2步,位数s>位数t的时候然后位数相等的时候利用DP,处理,分别就是枚举前k个数和s相同,然后k+1个数比t大就可以. 具体思路自己想想,和那个比较像   cons......
  • 【P4331 [BalticOI 2004]】Sequence 数字序列 题解(左偏树维护动态区间中位数)
    左偏树维护动态区间中位数。传送门P4331BalticOI2004Sequence数字序列。Solution1我的思路和题解前半部分完全重合了((如果按照单调不增去分割\(a\)序列的话,对于每一段我们能很简单地得出它的最佳答案:中位数。发现严格单调很难做,很难拿捏,考虑对\(a\)序列的每一项都进......
  • [AGC049D] Convex Sequence
    [AGC049D]ConvexSequence给定整数\(n\)和\(m\),问有多少个长为\(n\)的非负整数数列\(A\),满足以下条件:\(A_1+A_2+\ldots+A_n=m\)对任意\(i(2\leqi\leqN-1)\),都有\(2A_i\leqA_{i-1}+A_{i+1}\)答案对\(10^9+7\)取模。\(\texttt{datarange}\):\(n,m\le......
  • CF1794C Scoring Subsequences题解
    文中\(a\)为题目中给的\(a\)。如果我们要求\(a_1,a_2,a_3,\dots,a_m\)的结果,那么我们可以把\(a\)数组从后往前依次除以\(i\),\(i\)从\(1\)到\(n\),即为\(\frac{a_1}{m},\frac{a_2}{m-1},\frac{a_3}{m-2},\dots,\frac{a_{m-1}}{2},\frac{a_m}{1}\),并将其保......
  • 【题解】P4331 [BalticOI 2004]Sequence 数字序列
    以各种方式出现被玩烂的题目,算是小trick题?cpeditor意外地好用思路可并堆。平行时空同位体:CF13CP4331P4597CF713CP2893已知做法:\(O(n^2)\)dp:令\(f[i][j]\)为前\(i\)个数不超过\(j\)的最小代价优化:使用堆维护dp产生的折线(P4597题解区)\(O(n\logn......
  • Personalized Top-N Sequential Recommendation via Convolutional Sequence Embeddin
    目录概符号说明Caser代码TangJ.andWangK.Personalizedtop-nsequentialrecommendationviaconvolutionalsequenceembedding.WSDM,2018.概序列推荐的经典之作,将卷积用在序列推荐之上.符号说明\(\mathcal{U}=\{u_1,u_2,\cdots,u_{|\mathcal{U}|}\}\),us......
  • 「USACO2016JAN」Subsequences Summing to Sevens
    [USACO16JAN]SubsequencesSummingtoSevensS题目描述FarmerJohn's\(N\)cowsarestandinginarow,astheyhaveatendencytodofromtimetotime.EachcowislabeledwithadistinctintegerIDnumbersoFJcantellthemapart.FJwouldliketota......
  • ABC262Ex Max Limited Sequence 题解
    题意:给定\(m\)个限制\((l_i,r_i,p_i)\)及\(n,k\),求满足以下条件的长度为\(n\)的不同序列\(a=(a_1,a_2,\cdots,a_n)\)的数目。\(\foralli\in[1,n],0\leqa_i\leqk\)\(\foralli\in[1,m],\max\limits_{j\in[l_i,r_i]}a_j=p_i\)同P4229,但数据更强,目测只允......