Examples

2022-7-7 & 8 #8 AGC049E & AGC049D

铁难成钢 无妨 我本就是这样
而真实的愿望 只能在 废纸篓埋葬

太摆了太摆了啥时候能调整过来啊。😥

024 AGC049E Increment Decrement

首先考虑对于一个数组 \(a\),怎么求出其代价。

(第一步做反了,想成通过操作二最小化结果的绝对值之和了,难受啊)

操作一是更容易刻画的,在有两种操作的情况下,应该把更难刻画的操作作为结果(作为结果可以放低对操作的要求),将更容易刻画的操作作为过程。(感觉说的很混乱啊)

于是我们需要确定一个序列 \(b\),最小化:

\[\sum_{i=1}^n|a_i-b_i|+c\max(b_{i+1}-b_i,0) \]

可以直接列出 dp,令 \(f_{i,j}\) 表示考虑前 \(i\) 个位置,\(b_i=j\) 的最小代价。

\[f_{i,j}=\min_{k\geqslant 0}\{f_{i-1,k}+C\max(j-k,0)\}+|j-a_i|\\=\min_{k\geqslant 0}\{f_{i-1,k}+|a_{i-1}-k|+C\max(j-k,0)\} \]

通过归纳证明 \(f_{i,j}\) 为凸,具体我们考虑 \(f_{i-1}\) 转移到 \(f_i\) 时的变化,其加上了一个 \(g_i(x)=|a_{i-1}-x|\),然后和 \(h(x)=C\max(x,0)\) 进行了一次 \((\min,+)\) 卷积。

采用 slope trick 维护 \(0\) 处点值 \(v\) 以及其差分的转折点集合 \(S\),一开始集合为 \(C\)\(0\)。加上 \(g_i\) 会加入两个转折点 \(a_{i-1}\),而与 \(h\) 进行 \((\min,+)\) 卷积会将斜率为 \(-1\)\(C+1\) 的直线掰下来,答案减去 \(S\) 中最小值,然后弹掉 \(S\) 中最小值、最大值各一个即可。

然后考虑计数,对于每个值计算其作为最小值被删掉的次数之和,改变一下就是大于等于某个值的数被删掉的次数之和。我们可以将 \(<x\) 的数视为 \(0\)\(\geqslant x\) 的数视为 \(1\)(因为我们只关心 \(x\)),令 \(f_{i,j}\) 表示前 \(i\) 次操作,\(j\)\(1\) 的方案数,\(g_{i,j}\) 表示对应的删掉 \(1\) 总个数。

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn=55,mod=1000000007;
int n,c,k,sum,ans,nums;
int a[maxn][maxn],b[maxn*maxn],f[maxn][maxn],g[maxn][maxn];
int calc(int d){
	memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		int t1=0;
		for(int j=1;j<=k;j++)
			t1+=a[i][j]>=d;
		for(int j=0;j<=c;j++){
			f[i][max(j-1,0)]=(f[i][max(j-1,0)]+1ll*f[i-1][j]*(k-t1))%mod;
			f[i][min(j+1,c)]=(f[i][min(j+1,c)]+1ll*f[i-1][j]*t1)%mod;
			g[i][max(j-1,0)]=(g[i][max(j-1,0)]+1ll*g[i-1][j]*(k-t1))%mod;
			g[i][min(j+1,c)]=(g[i][min(j+1,c)]+1ll*g[i-1][j]*t1)%mod;
		}
		g[i][c]=(g[i][c]+1ll*f[i-1][c]*t1)%mod;
	}
	int res=0;
	for(int i=0;i<=c;i++)
		res=(res+g[n][i])%mod;
	return res;
}
int main(){
	scanf("%d%d%d",&n,&c,&k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=k;j++)
			scanf("%d",&a[i][j]),sum=(sum+a[i][j])%mod,b[++nums]=a[i][j];
	for(int i=1;i<n;i++)
		sum=1ll*sum*k%mod;
	sort(b+1,b+1+nums);
	for(int i=1;i<=nums;i++)
		ans=(ans+1ll*(b[i]-b[i-1])*calc(b[i]))%mod;
	printf("%d\n",(sum-ans+mod)%mod);
	return 0;
}

025 AGC049D Convex Sequence

明明没搞什么文化,思维还是很迟钝,真是碰了👻了。原来是看错题了那没事了。

显然中间有一段最小值,然后向左向右都是凸且递增的。

枚举一下最小值最靠左的位置(即差分数组为 \(0\) 的位置),发现其实就是左边能选一个前缀差分数组减一(最小值左边强制操作一次),右边能选一个后缀差分数组加一。由于是 \(+1,+2,\cdots,+k\) 形式,有效的操作只有根号种,做一个完全背包就好了。

每次都要提取所有 \(m-kn\) 位置的值,放一个 \(n\) 进完全背包即可。

复杂度 \(O(n\sqrt n)\)

#include<stdio.h>
const int maxn=100005,mod=1000000007;
int n,m,ans;
int f[maxn];
void add(int x){
	for(int i=x;i<=m;i++){
		f[i]+=f[i-x];
		if(f[i]>=mod)
			f[i]-=mod;
	}
}
void del(int x){
	for(int i=m;i>=x;i--){
		f[i]-=f[i-x];
		if(f[i]<0)
			f[i]+=mod;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	f[0]=1,add(n);
	for(int i=1;i<n&&i*(i+1)/2<=m;i++)
		add(i*(i+1)/2);
	for(int i=1;i<=n;i++){
		if(1ll*i*(i-1)/2<=m){
			ans+=f[m-i*(i-1)/2];
			if(ans>=mod)
				ans-=mod;
		}
		if(1ll*(n-i)*(n-i+1)/2<=m)
			del((n-i)*(n-i+1)/2);
		if(1ll*i*(i+1)/2<=m)
			add(i*(i+1)/2);
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-07-07 21:36  xiaoziyao  阅读(114)  评论(0)    收藏  举报