HZNU2019校赛-Little Sub and Counting(小撒布与计数)(函数的单调性)
Description
Little Sub likes Math. Now he has a simple counting problem for you.
小撒布喜欢数学,现在他有个简单问题要问你。
Given an positive integer sequence A, for each Ai, Please calculate how many elements Aj in the sequence satisfied Ai^Aj > Aj^Ai.
给定一个正整数序列A,对于每个Ai,请计算多少个序列内的Aj使得 Ai^Aj > Aj^Ai成立。
Input
The first line contains one positive integer n(1 ≤ n ≤ 100000).、
第一行包含一个正整数n
The following line contains n positive integers Ai(1 ≤ Ai ≤ 2^31 − 1).
接下来是n个正整数ai
Output
Please output n integer in one line and separate them by a single space. The ith integer indicates the answer concerning Ai.
在一行内输出n个整数。第i个整数是有关Ai的答案。
Samples
input:
3 1 2 5
output:
0 2 1
乍一看这个式子很要命——你中有我,我中有你。但是凭借直觉把两侧取对数,从而把指数变成乘积关系时,就有了一个很有意思的关系:
很明显这是一个函数,那么问题就变成了:
对于函数,当给定x的定义域(大于等于1的非连续无序数集)时,他们分别大于多少个函数值?
对函数进行求导,可知函数在x∈(0,e]时单调递增,x∈[e,正无穷]的时候单调递减。根据计算:f(3)为最大值,且f(2)=f(4)。
所以得到如下结论:
- f(3)一定大于除了其余f(3)之外的所有函数值
- f(1)等于0,且值域内其他数都大于0,所以f(1)肯定最小。
- 根据函数单调性f(2)=f(4),大于除了f(3),f(4),f(2)的值。
那么就得到了一些特殊关系(count为某个数的个数):
ans(1)=0,ans(2)=n-count(3)-count(2)-count(4),ans(3)=n-count(3),ans(4)=ans(2)
至于大于4的点,就是根据单调性逐一递减(相同的数除外)了,除了递推,我们还可以用两个神奇的二分函数:
注意:有序时才可用
std::lower_bound(起始地址,终止地址,值),这个函数返回一个地址,代表第一个大于或等于这个值的数的地址。
std::upper_bound(起始地址,终止地址,值),这个函数同样返回一个地址,代表第一个严格大于这个值的数的地址。
顺便一说,如果用返回的地址减去数组首地址,那这个位置就变成了数组下标。
当然由于这个题里面恶心的输出模式,我们不能直接把原数组给排序了,所以要复制一个副本,以副本为基准进行排序并二分。
所以有如下思路
- 根据数学知识解得函数的单调性,找出规律,列举所有特殊点的关系
- 复制原数组,对副本进行排序
- 利用二分函数与特殊点的关系,对每个点进行遍历,找到属于每个点的答案。
代码如下:
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const long long int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline int Unnamed_Scanner()
{
int tmp=0,si=1;
char c=getchar();
while(c>'9'||c<'0')
{
if(c=='-')
si=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
tmp=tmp*10+c-'0';
c=getchar();
}
return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
ll ans[5266465];
ll ans2[5266465];
ll cnt[5];
int DETERMINATION()
{
ll n;
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>ans[i];
if(ans[i]<=4)
cnt[ans[i]]++;//统计这些特殊值的个数
}
memcpy(ans2,ans,sizeof(ans));//复制数组
sort(ans2+1,ans2+n+1);//对副本排序
for(int i=1;i<=n;i++)
{
if(ans[i]==1)//特殊值的相关操作
cout<<0<<" ";
else if(ans[i]==2)
cout<<n-cnt[2]-cnt[3]-cnt[4]<<" ";
else if(ans[i]==3)
cout<<n-cnt[3]<<" ";
else if(ans[i]==4)
cout<<n-cnt[2]-cnt[3]-cnt[4]<<" ";
else
cout<<n+cnt[1]-(lower_bound(ans2+1,ans2+1+n,ans[i]+1)-ans2-1)<<" ";
//一般值的处理是:总数减去比他大的值的数组下标,再把1加上(多减了1,因为f(1)是最小值所以不能减去)
if(i==n)
cout<<endl;
}
return 0;
}

浙公网安备 33010602011771号