2020.10.7集训总结

本文探讨了两个计算机科学问题:一是关于矩阵对角线上小正方形的数量与矩阵尺寸的关系,通过数学分析得出计算公式;二是关于序列修正的最小代价问题,通过对序列差值的二次函数优化来寻找最小代价。这两个问题都涉及到数学优化和算法设计。

count

description

设矩阵 RRR 是由若干个正方形所组成的矩阵,定义函数 F(R)F(R)F(R) 为矩阵 RRR 的某一条对角线穿过的小正方形的个数。现在已知 F(R)=NF(R)=NF(R)=N,求可能的矩阵 RRR 的个数。

n≤109n\leq 10^9n109数据随机生成

solution
设一个矩形 R=n×m,m≤nR=n\times m,m\leq nR=n×m,mn,显然他对角线经过了 gcd⁡(n,m)−1\gcd(n,m)-1gcd(n,m)1 个格点,把他分成了 gcd⁡(n,m)\gcd(n,m)gcd(n,m) 个相同的小矩形。

那么我们只需要考虑一个 ngcd⁡(n,m)×mgcd⁡(n,m)\dfrac{n}{\gcd(n,m)}\times\dfrac{m}{\gcd(n,m)}gcd(n,m)n×gcd(n,m)m 的矩形的交点个数

我们发现他在纵方向上显然至少要经过 ngcd⁡(n,m)\dfrac{n}{\gcd(n,m)}gcd(n,m)n 个格子,而纵坐标方向因为不交在格点上,所以只会多增加 mgcd⁡(n,m)−1\dfrac{m}{\gcd(n,m)}-1gcd(n,m)m1 个格子

所以我们可以推出

F(R)=gcd⁡(n,m)×(ngcd⁡(n,m)+mgcd⁡(n,m)−1)=n+m−gcd⁡(n,m)F(R)=\gcd(n,m)\times(\dfrac{n}{\gcd(n,m)}+\dfrac{m}{\gcd(n,m)}-1)=n+m-\gcd(n,m)F(R)=gcd(n,m)×(gcd(n,m)n+gcd(n,m)m1)=n+mgcd(n,m)

已知 F(R)=NF(R)=NF(R)=N

也就是 n+m−gcd⁡(n,m)=Nn+m-\gcd(n,m)=Nn+mgcd(n,m)=N

发现枚举 gcd 的话时间会炸,所以我们考虑把 gcd 除掉

ngcd⁡(n,m)+mgcd⁡(n,m)−1=Ngcd⁡(n,m)\dfrac{n}{\gcd(n,m)}+\dfrac{m}{\gcd(n,m)}-1=\dfrac{N}{\gcd(n,m)}gcd(n,m)n+gcd(n,m)m1=gcd(n,m)N

也就是 gcd⁡(n,m)\gcd(n,m)gcd(n,m) 只有可能是 NNN 的因数。

f(x)f(x)f(x) 表示 i+j=x,gcd⁡(i,j)=1i+j=x,\gcd(i,j)=1i+j=x,gcd(i,j)=1(i,j)(i,j)(i,j) 的个数,那么问题转化成了求

∑d∣Nf(d+1)\sum_{d|N}f(d+1)dNf(d+1)

考虑如何求 f(x)f(x)f(x)。观察发现,如果 gcd⁡(i,x)=1\gcd(i,x)=1gcd(i,x)=1 时,显然满足条件, 否则不满足条件,也就是转化成了求与 xxx 互质的数的个数,即 f(x)=ϕ(x)f(x)=\phi(x)f(x)=ϕ(x)

也就是转化成了求

∑d∣Nϕ(d+1)\sum_{d|N}\phi(d+1)dNϕ(d+1)

我们枚举 NNN 的因数,每次暴力计算,复杂度是 O(d(n)⋅n)O(d(n)\cdot \sqrt n)O(d(n)n),其中 d(n)d(n)d(n) 表示 nnn 的因数个数,计算发现 10910^9109 内因数个数最多的也只有 1300 多个,并且很难跑满,加上数据随机生成,就可以通过了。

code

#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=1e4+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

ll n;
int fac[N],tot;
ll ans;

ll phi(int x){
	ll res=x;
	for(int i=2;i*i<=x;i++){
		if(x%i==0)res=res/i*(i-1);
		while(x%i==0)x/=i;
	}
	if(x>1)res=res/x*(x-1);
	return res;
}

int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	read(n);
	for(int i=1;i*i<=n;i++){
		if(n%i==0){
			fac[++tot]=i;
			if(n/i!=i)fac[++tot]=n/i;	
		}
	}
	Rep(i,1,tot)ans+=phi(n/fac[i]+1);
	printf("%lld\n",(ans+1)/2);
	return 0;
}

seq

description

给出一个长度为 NNN 的正整数数列 {Ai}\{A_i\}{Ai},现在可以给数列中的每个数加上任意的正整数增量,也可以不加,给一个数加上 XXX 的代价为 X2X^2X2。在修正后的数列中,计算每两个相邻整数的差的绝对值,将它乘以 CCC 计入代价中。求最小代价。

n≤105n\leq 10^5n105

solution

首先考虑 333 个数的最简单情况,我们想要修改中间的数的条件应该是 ai−1>ai<ai+1a_{i-1}>a_i<a_{i+1}ai1>ai<ai+1,否则修改代价增加,差值代价不变,显然不优。

同时我们发现,aia_iai的最终取值范围应当在 [ai,min⁡(ai−1,ai+1)][a_i,\min(a_{i-1},a_{i+1})][ai,min(ai1,ai+1)] 上,如果再变大,修改代价增加,差值代价不变,显然不优。

考虑 444 个数的时候,同样的,中间能够被修改的前提也是两边比他大。

考虑中间被修改的值能够是多少,假设中间两个数调整后 B<CB<CB<C,那么把 C−1C-1C1,修改代价减小,差值代价不变,显然更优。

我们可以证明,对于一个区间 [i,j][i,j][i,j] 能够被修改的条件,就是 RMQ⁡(i+1,j−1)≤min⁡(ai,aj)\operatorname{RMQ}(i+1,j-1)\leq \min(a_i,a_j)RMQ(i+1,j1)min(ai,aj),并且 [i+1,j−1][i+1,j-1][i+1,j1] 之间的数修改完的值相等,且在 [RMQ⁡(i+1,j−1),min⁡(ai,aj)][\operatorname{RMQ}(i+1,j-1),\min(a_i,a_j)][RMQ(i+1,j1),min(ai,aj)] 上。

那么如何确定在哪里取最小值呢?我们把它写成一个函数的形式,假设这个区间为 [l,r][l,r][l,r],最后高度为 xxx,代价为 yyy

y=(r−l−1)x2−(2∑i=l+1r−1ai−2c)x+(ai+aj)c+∑i=l+1r−1ai2y=(r-l-1)x^2-(2\sum_{i=l+1}^{r-1}a_i-2c)x+(a_i+a_j)c+\sum_{i=l+1}^{r-1}a_i^2y=(rl1)x2(2i=l+1r1ai2c)x+(ai+aj)c+i=l+1r1ai2

相当于转化成二次函数的最值问题。

同时我们发现,这个转移对于每一个 iii 是只能从 jjj 满足 RMQ⁡(i+1,j−1)≤min⁡(ai,aj)\operatorname{RMQ}(i+1,j-1)\leq \min(a_i,a_j)RMQ(i+1,j1)min(ai,aj) 转移过来的,这个是满足单调栈性质的,所以我们可以用一个单调栈来进行维护转移一个 dpdpdp

注意边界问题和一些细节。

复杂度 O(nlog⁡n+n)O(n\log n+n)O(nlogn+n)

#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=1e5+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,m;
int a[N];
ll f[N];
int st[N][20],lg[N];
ll sum1[N],sum2[N];
int s[N],top;

int RMQ(int l,int r){
	int len=r-l+1;
	return max(st[l][lg[len]],st[r-(1<<lg[len])+1][lg[len]]);
}

ll cost(int l,int r){
	if(r==l+1)return 1ll*m*abs(a[r]-a[l]);
	ll A=r-1-l,B=-2*(sum1[r-1]-sum1[l]),C=sum2[r-1]-sum2[l];
	if(l!=0)B-=m,C+=1ll*a[l]*m;
	if(r!=n+1)B-=m,C+=1ll*a[r]*m;
	int mn=RMQ(l+1,r-1),mx=min(a[l],a[r]);
	ll ver=-B/(2*A),res=1e18;
	Rep(i,-1,1)
		if(ver+i>=mn&&ver+i<=mx)res=min(res,A*(ver+i)*(ver+i)+B*(ver+i)+C);
	res=min(res,A*mn*mn+B*mn+C);
	res=min(res,A*mx*mx+B*mx+C);
	return res;
}

int main()
{
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	memset(f,0x3f,sizeof(f));
	read(n),read(m);
	Rep(i,1,n)read(a[i]);
	lg[1]=0;
	Rep(i,2,n)lg[i]=lg[i>>1]+1;
	Rep(i,1,n)st[i][0]=a[i];
	Rep(j,1,19)
		Rep(i,1,n)
			if(i+(1<<j-1)<=n)st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
	Rep(i,1,n)sum1[i]=sum1[i-1]+a[i];
	Rep(i,1,n)sum2[i]=sum2[i-1]+1ll*a[i]*a[i];
	a[0]=a[n+1]=1e9;
	s[++top]=0;
	s[++top]=1;
	f[0]=f[1]=0;
	Rep(i,2,n+1){
		if(i!=n+1)f[i]=min(f[i],f[i-1]+1ll*m*abs(a[i]-a[i-1]));
		else f[i]=f[i-1];
		while(a[s[top]]<a[i]){
			f[i]=min(f[i],f[s[top]]+cost(s[top],i));
			top--;
		}
		f[i]=min(f[i],f[s[top]]+cost(s[top],i));
		s[++top]=i;
	}
	printf("%lld\n",f[n+1]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值