题目链接:点击查看
题目大意:首先定义 “ n 的拆分 ” 是 n = a[ 1 ] + a[ 2 ] + ... + a[ m ] ,在本题中,n 的拆分需要满足几个条件:
- a[ i ] <= a[ i + 1 ] <= a[ i ] + 1 , i ∈ [ 1 , m ]
- 1 <= a[ i ] <= n , i ∈ [ 1 , m ]
- a[ m ] = a[ 1 ] + 2
设 f( n ) 为满足上述所有条件下,n 的拆分有多少种,现在给出 t 组查询,每组查询给出一对 [ l , r ] ,求 f( l ) + f( l + 1 ) + ... + f( r )
题目分析:官方题解的做法是枚举 l ,因为每个数一定由连续的 l , l + 1 和 l + 2 组成,暴力去维护 f( n ) ,不过我没看懂,这里就不多展开了
还有一种更加优秀的做法,暂且叫他二阶差分吧,注意是二阶差分,不是二维差分
参考博客:https://www.cnblogs.com/rair/p/13430729.html
(最后一个图的num2应该打在13的位置,昨晚上没检查出来,不要被误导(狗头))
最后再说一下四个数的特征:
- num1:111...123
- num2:122...223
- num3:num2 + 2
- num4: 123...333 + 3
代码:
#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<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
LL sum[N*10];
void init()
{
for(int i=1;i<N;i++)//枚举a[1],a[m]=a[1]+2
for(int m=3;m*i<N;m++)//枚举a[1],a[2]...a[m]的m
{
int num1=i*m+3,num2=(i+1)*m+1;
int num3=num2+1,num4=(i+2)*m-3+3;
sum[num1]++,sum[num2]--;
sum[num3]--,sum[num4]++;
}
for(int i=2;i<N;i++)//二阶差分(隔项)
sum[i]+=sum[i-2];
for(int i=2;i<N;i++)//一阶差分(还原f(n))
sum[i]+=sum[i-1];
for(int i=2;i<N;i++)//前缀和
sum[i]+=sum[i-1];
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
int w;
cin>>w;
int kase=0;
while(w--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("Case #%d: %lld\n",++kase,sum[r]-sum[l-1]);
}
return 0;
}