本题结论题,所以就不放前置芝士了.
具体做法
先将最终的答案分为两部分,区间(开始于结束为止不同)和点,点的个数非常显然就是M,于是要计算区间的个数,可以发现如果直接计算有多少合法区间很麻烦,所以用总共的区间数 N ∗ ( N − 1 ) 2 \frac{N*(N-1)}{2} 2N∗(N−1),减去没有的部分,可以发现这M个1可以将这个区间分成M+1段0(长度可以为0),两段之间有1的0之间不会相互影响,所以没有的部分就是每一段0中的区间个数之和,对于每一段0中的区间个数,设一个函数 f ( x ) = x ∗ ( x − 1 ) 2 f(x)=\frac{x*(x-1)}{2} f(x)=2x∗(x−1),可以发现这是一个二次函数,然后经过感谢理解,当每一段长度尽可能相等时最终的没有0的区间个数才是最少,所以可以计算出两个数 n u m 1 num1 num1和 n u m 2 num2 num2, n u m 1 num1 num1为长度为 ⌊ N − M M + 1 ⌋ + 1 \lfloor \frac{N-M}{M+1} \rfloor+1 ⌊M+1N−M⌋+1的连续0的段数, n u m 2 num2 num2为长度为 ⌊ N − M M + 1 ⌋ \lfloor \frac{N-M}{M+1} \rfloor ⌊M+1N−M⌋的连续0的段数,然后将长度带入函数,再乘上相应的区间数,在总区间数中减去,就可以知道至少含有一个1的区间个数了,最终答案只要再加上一个M就好了.
代码
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
long long N,M;
int T;
void work()
{
scanf("%lld%lld",&N,&M);
long long len=(N-M)/(M+1);//计算出长度
long long num1=(N-M-len*(M+1));//因为这个长度是向下取整的随意可能有剩余,而长度为len+1的个数自然就是剩余的0的个数了
long long num2=M+1-num1;//因为总共有M+1段
long long answer=N*(N-1)-len*(len+1)*num1-len*(len-1)*num2;//计算
answer/=2;//前面没有除二
printf("%lld\n",answer+M);//最后答案加上M之后输出
}
int main()
{
scanf("%d",&T);
REP(i,1,T)
work();
return 0;
}