- 决策单调性分治
容易发现将询问离线排序之后,值更大的询问能取到的最优决策点是递增的。 这是因为考虑a数组,如果出现一个逆序对,那么右边那个位置一定是不优的; 因此决策点一定是一个下标递增,值也递增的数列。
决策单调性考虑分治,对于一个询问的值 ,只需枚举当前决策区间内的每个点
,答案是
中比
大的和
中比
大的数个数的和。
使用主席树查询区间中比某个数大的个数即可。
复杂度两个 。
///*****Sellaris*****///
#include <bits/stdc++.h>
//#include <bits/extc++.h>
#include<ext/rope>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>//用tree
#include<ext/pb_ds/hash_policy.hpp>//用hash
#include<ext/pb_ds/trie_policy.hpp>//用trie
#include<ext/pb_ds/priority_queue.hpp>//用priority_queue
#define ll long long
#define x first
#define y second
typedef unsigned long long ull;
typedef std::pair<int,int> pii;
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
const int maxn=2e5+10;
const int mo=1e9+7;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch)){ret=ret*10+ch-'0';ch=getchar();}
return ret*f;
}
int n,m;
int a[maxn];
pii q[maxn];
int ans[maxn];
struct tree{
int l,r,sum;
}tr[maxn*40];
int tot,root[maxn];
inline int build(int &p,int l,int r){
p=++tot;
if(l==r) return 1;
int mid=(l+r)>>1;
build(tr[p].l,l,mid);
build(tr[p].r,mid+1,r);
return 1;
}
inline void modify(int &p,int q,int l,int r,int k){
p=++tot;
tr[p]=tr[q];
tr[p].sum++;//push_up
if(l==r) return ;
int mid=(l+r)>>1;
if(k<=mid) modify(tr[p].l,tr[q].l,l,mid,k);
else modify(tr[p].r,tr[q].r,mid+1,r,k);
}
inline int query(int p,int q,int l,int r,const int k){
if (l==r) return tr[q].sum-tr[p].sum;
int mid=(l+r)>>1;
if (mid>=k)
return tr[tr[q].r].sum-tr[tr[p].r].sum + query(tr[p].l,tr[q].l,l,mid,k);
else
return query(tr[p].r,tr[q].r,mid+1,r,k);
}
pii clac(int x,int l,int r){
int v=q[x].x;
int ans=1e9,pos;
for(int i=l;i<=r;i++){
int val=query(root[0],root[i-1],1,n+m,v+1) + query(root[i],root[n],1,n+m,max(v,a[i])+1);
if(val<=ans) pos=i;
ans=min(ans , val);
//cout<<"I====="<<i<<"\n";
//cout<<"q="<<v<<" val="<<val<<"\n";
//cout<<"q[0,i-1]"<<" "<<query(root[0],root[i-1],1,n+m,v)<<"\n";
//cout<<"q[i+1,n]"<<" "<<query(root[i],root[n],1,n+m,a[i])<<"\n";
}
return {ans,pos};
//query(root[0],root[i-1],1,n+m,v)
//query(root[i],root[n],1,n+m,a[i])
}
void dac(int l,int r,int ansl,int ansr){
int mid=(l+r)>>1;
auto [v,pos]=clac(mid,ansl,ansr);
ans[q[mid].y]=v;
//l~mid-1 mid+1~r
//cout<<"query="<<q[mid].x<<" l="<<ansl<<" r="<<ansr<<" \n";
//cout<<"ans="<<v<<"\n";
if(l<=mid-1)dac(l,mid-1,ansl,pos);
if(r>=mid+1)dac(mid+1,r,pos,ansr);
}
vector<int>lsh;
inline void solve(){
n=read();
for(int i=1;i<=n;i++)a[i]=read(), lsh.push_back(a[i]);
m=read();
for(int i=1;i<=m;i++){
int v=read();
q[i]={v,i};
lsh.push_back(v);
}
sort(lsh.begin(),lsh.end());
lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
for(int i=1;i<=n;i++){
a[i]=lower_bound(lsh.begin(),lsh.end(),a[i])-lsh.begin()+1;
//cout<<a[i]<<" \n"[i==n];
}
sort(q+1,q+1+m);
for(int i=1;i<=m;i++){
q[i].x=lower_bound(lsh.begin(),lsh.end(),q[i].x)-lsh.begin()+1;
//cout<<q[i].x<<"\n";
}
build(root[0],1,n+m);
for(int i=1;i<=n;i++){
modify(root[i],root[i-1],1,n+m,a[i] );
}
//query(root[x-1],root[y],1,n+m,k)
dac(1,m,1,n);
for(int i=1;i<=m;i++){
cout<<ans[i]<<"\n";
}
}
signed main(){
//std::ios::sync_with_stdio(false);std::cin.tie(NULL);std::cout.tie(NULL);
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t=1;
for(int i=1;i<=t;i++){
// cerr<<"-------------------------\n";
// cerr<<"test case "<<i<<"\n";
solve();
// cerr<<"-------------------------\n";
}
return 0;
}
/*想想你要干什么?
* 效率:15min思考,修改思路!
* 正难则反
* 明确数组定义
* 可能的思路
* 特殊情况 ( n == 1 )?
* 查看溢出,数组越界
* 不要在一个方面卡死
* 多想性质!
while(1){
system("data.exe > in.txt");
system("C.exe < in.txt > std.txt");
system("C2.exe < in.txt > baoli.txt");
if(system("fc baoli.txt std.txt")) system("pause");
}
7
6 1 2 3 4 7 8
9
1 2 1 2 5 5 6 3 4
6
1 2 3 2 3 2
5
5
4
2
4
5
*/