202112-2 序列查询新解
题目描述
题目分析
我以第一个输入举例,第一次用二分查找可以找出f[x]对应的值g[x] = x / r ,所以依题意可以很容易写出来。
x 0 1 2 3 4 5 6 7 8 9
A 0 2 5 8 10
i 0 1 2 3
fx0 0 1 1 1 2 2 2 3 3
gx0 0 1 1 2 2 3 3 4 4
二分模板
void erfen1()
{
while (l < r)
{
int mid = l + r >> 1; //(l+r)/2
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
}
void erfen2()
{
while (l < r)
{
int mid = l + r + 1 >> 1; //(l+r+1)/2
if (check(mid)) l = mid;
else r = mid - 1;
}
}
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std ;
const int M = 10010;
int n , N ;
int A[M] , f[M] , g[M];
int ff(int x)
{
int l = 0 , r = n ;
while ( l < r)
{
int mid = (l + r + 1) >> 1 ;
if (A[mid] <= x){
l = mid ;
} else{
r = mid - 1 ;
}
}
return l ;
}
int main()
{
cin >> n >> N ;
A[0] = 0 ;
for (int i = 1 ; i <= n ; ++i)
{
cin >> A[i] ;
}
int res = 0 ;
int r = N / (n + 1) ;
for (int i = 0 ; i < N ; ++i)
{
f[i] = ff(i) ;
g[i] = i / r ;
res += abs(g[i] - f[i]) ;
}
// int res = 0 ;
// for (int i = 0 ; i < N ; ++i)
// {
// res += abs(g[i] - f[i]) ;
// }
cout << res ;
return 0 ;
}
题目分析
上述答案为70分,通过分析可以知道,二分的时间复杂度为O(nlogn),0<=N<=1e9,如果按照O(N * nlogn),1<=n<=1e5,因此遍历条件不能为N,我们选择用n来遍历,也就是选择点。
x 0 1 2 3 4 5 6 7 8 9
A 0 2 5 8 10
i 0 1 2 3
fx0 0 1 1 1 2 2 2 3 3
gx0 0 1 1 2 2 3 3 4 4
通过上述样例分析出,当r = N / (n + 1) = 2时,fx和gx都按照一定的规律进行变化,并且我们在遍历点的时候,fx=i,gx在A[i]-A[i+1]-1中变化,for (ll j = A[i] ; j <= A[i+1] - 1; j += dr ),gx = j / r ;值得我们注意的是,当区间不足r时,fx和gx的值是不一样的,并且影响了最后一段和下一段的开始,我们用r和A[i+1]来进行判断,如果j+r -1 > A[i+1] - 1,也就是j + dr > A[i+1],说明在当前区间最后一段只有小于r的一个区间,那么我只需要加上A[i+1] - j的长度* res,完成此操作后,还要对下一段的开始进行操作,也就是dr-(A[i+1] - j),在操作完之后,下一个区间的增加长度又为r了。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std ;
typedef long long ll ;
const int nn = 100010 ;
ll A[nn] , f , g;
int N , n;
int main()
{
cin >> n >> N ;
A[0] = 0 ;
for (int i = 1 ; i <= n ; ++i)
{
cin >> A[i] ;
}
A[n + 1] = N ;
ll r = N / (n + 1) ;
int dr = r ;
ll error = 0;
int ddr ;
int flag = 0;
for (int i = 0 ; i < n + 1; ++i)
{
f = i ;
// cout << f << endl ;
for (ll j = A[i] ; j <= A[i+1] - 1; j += dr )
{
// cout << j << "->" << A[i+1] - 1 << endl;
g = j / r ;
ll res = f > g ? f-g : g-f ;
// cout << sum << endl ;
if (flag == 1)
{
dr = ddr ;
flag = 0 ;
} else {
dr = r ;
}
if (j + dr<= A[i+1]) {
error += (res * dr) ;
}
if (j + dr > A[i+1] ){
error += (A[i+1] - j) * res ;
ddr = dr - (A[i+1] - j) ;
flag = 1 ;
}
}
}
cout << error <<endl;
return 0 ;
}
总结
题目不难,但是自己还是只写出了70分的答案,刚开始想的用离散化,但还是会用二分,本质上没有区别,最后,提醒一下自己一定要认真审题!!!!!