题目链接:点击查看
题目大意:规定函数 f(s) 为01字符串 s 中至少包含一个 1 的所有子串的数量,比如 f(10101) = 12 ,其中十二个子串分别为(1,2),(1,3),(1,4),(1,5),(2,2),(2,3),(2,4),(2,5),(3,4),(3,5),(4,4),(4,5),现在给出一个01字符串的长度为 n ,其中包含了 m 个 1 ,问所有情况中 f(s) 的最大值
题目分析:通过样例不难看出,如果想要使得 f(s) 最大,那么必须使得 0 的连续部分尽量多,而这个题正难则反,直接求至少包含 1 的子串比较难求,我们可以考虑用总的子串个数减去只含有 0 的子串个数,而对于一个字符串而言,其子串的个数为:1+2+3+...+n-1+n=(n+1)*n/2,而一个字符串中有 m 个 1 ,相应的可以将 (n-m) 个 0 分割为 (m+1) 个区间中,其中:
- 有 ( n - m ) % ( m + 1 ) 个区间中含有 ( n - m ) / ( m + 1 ) 个 0
- 有( m + 1 ) - [ ( n - m ) % ( m + 1 ) ] 个区间中含有 ( n - m ) / ( m + 1 ) + 1 个 0
然后直接计算就好了,上面的等式看似很复杂,但自己推一下其实也就那回事
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e3+100;
LL C(LL n)
{
return n*(n+1)/2;
}
int main()
{
//#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
//#endif
// ios::sync_with_stdio(false);
int w;
cin>>w;
while(w--)
{
LL n,m;
scanf("%lld%lld",&n,&m);
LL zero=n-m;//有这么多 0,共分成了 m+1 段
LL t=zero/(m+1);//每段 0 含有 t 个 0
LL k2=zero%(m+1);//有k2段 0 含有 t+1 个 0
LL k1=(m+1)-k2;//有k1段 0 含有 t 个 0
printf("%lld\n",C(n)-C(t)*k1-C(t+1)*k2);
}
return 0;
}