代码源每日一题div1 Ayoub‘s function

Daimayuan Online Judge

思路:思维
我们从问题的反面考虑,就是要求所有的子串减去只含 0 0 0的子串,那问题转化成如何让只含 0 0 0的子串尽可能的少,结论是将 1 1 1尽可能的均匀的分布在这个字符串中
显然我们有 m m m 1 1 1就会分割出 m + 1 m+1 m+1个只含 0 0 0的区间,那我们只要用总的子串个数减去这些只含 0 0 0的区间的子串即可
一个长度为 l l l的字符串的子串个数为: l ∗ ( l + 1 ) / 2 l*(l+1)/2 l(l+1)/2
现在我们来证明一下这个结论,为了方便我们只考虑整除的情况,即这些 1 1 1划分的 0 0 0区间长度相同
我们定义字符串长度 n n n 1 1 1的个数 m m m,那么只含 0 0 0的区间个数为 m + 1 m+1 m+1,这些区间的长度为 k = n − m m + 1 k=\frac{n-m}{m+1} k=m+1nm,那么只含 0 0 0的子串个数 c n t = ( m + 1 ) ∗ 1 2 ∗ k ∗ ( k + 1 ) cnt=(m+1)*\frac{1}{2}*k*(k+1) cnt=(m+1)21k(k+1),现在我们将其中一个 1 1 1向一侧移动一个位置,那么会导致两侧的两个 0 0 0长度分别 − 1 -1 1 + 1 +1 +1,那么子串个数变为 c n t ’ = ( m − 1 ) ∗ 1 2 ∗ k ∗ ( k + 1 ) + 1 2 ∗ ( k + 1 ) ∗ ( k + 2 ) + 1 2 ∗ ( k − 1 ) ∗ k cnt’=(m-1)*\frac{1}{2}*k*(k+1)+\frac{1}{2}*(k+1)*(k+2)+\frac{1}{2}*(k-1)*k cnt=(m1)21k(k+1)+21(k+1)(k+2)+21(k1)k,做差比较: c n t − c n t ′ = 1 2 ( 2 ∗ k ∗ ( k + 1 ) − ( k + 1 ) ∗ ( k + 2 ) − k ∗ ( k − 1 ) ) = − 1 < 0 cnt-cnt'=\frac{1}{2}\Big(2*k*(k+1)-(k+1)*(k+2)-k*(k-1)\Big)=-1<0 cntcnt=21(2k(k+1)(k+1)(k+2)k(k1))=1<0,因此 c n t < c n t ′ cnt<cnt' cnt<cnt得证

Code:

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl '\n';
typedef long long ll;
typedef pair<ll,ll> PII;
const int N=1e6+10;
ll n,m,t;
int main(){
	IOS;
	cin>>t;
	while(t--){
		cin>>n>>m;
		ll ans=n*(n+1)/2,x=(n-m)/(m+1),y=(n-m)%(m+1);
		if((n-m)%(m+1)==0) ans-=(m+1)*x*(x+1)/2;
		else ans-=((m+1-y)*x*(x+1)/2+y*(x+1)*(x+2)/2);
		cout<<ans<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值