codeforces1024 23 Kingdom 题解
题目链接:
https://codeforces.com/contest/2102/problem/E
思路:
1.首先我要让每个值的贡献最大,我就必须要让它出现的位置尽量靠近数组的两端,这样的距离最远,贡献最大。
2.然后因为每个位置的数值能填的值是有上限的,我用\(1,2,3,4\)这样的数来构造显然优于用\(1,3,4,5\)这样的数来够造。
3.(数组位置\(a_{l_1}\)位置\(a_{r_1}\)位置的数填同个值然后\(a_{l_2}\)位置和\(a_{r_2}\)位置填同个值)
等价于
(数组位置\(a_{l_1}\)位置\(a_{r_2}\)位置的数填同个值然后\(a_{l_2}\)位置和\(a_{r_1}\)位置填同个值)。
换句话说就是只要我确定了左边的端点在哪些位置,右边的端点在哪些位置,他们无论内部怎么配对,贡献都是一样的也就是\(\sum r_i\) \(-\) \(\sum l_i\)。
所以基于这个我就可以双指针从数组的两端的类似反悔贪心的构造。
就是我先从小到大的依次填配对的填,但如果遇到了某个值是1,之前1已经用了,但之前出现过比1大的,那你就可以反悔假设前面填的是2,现在这里填的是1。根据之前的结论,这样的是等价的。
那这个过程相当于维护两个集合\(L,R\)。维护可以用来填的的备选值的集合\((\)集合里每种数只能出现一次\()\)。但这里注意一个事情,假设插入位置的值等于\(4\),但\(4\)之前已经插入了,因为可以填小于等于\(4\)。显然的是越大的数越好,因为可以向下兼容当成小的数用。所以插入的时候就是插入在小于等于且尽量靠近\(v_i\)的位置。这一过程可以用线段树维护,插入失败说明这个位置的数做不了贡献。
双指针模拟,如果现在左边集合的大小小于目前填的值\(minn\),就移动左指针继续插入,右边同理。如果两边的集合大小都等于\(minn\)了,\(minn\)++然后\(ans\)加上\(r-l\)的贡献即可。(为什么是集合大小呢,因为大的值能当小的用。)
代码:
#include<bits/stdc++.h>
using namespace std;
#define ls 2*i
#define rs 2*i+1
#define int long long
const int N=200010;
int v[N];
int tree1[N<<2];
int tree2[N<<2];
void up(int i,int* tree){
tree[i]=(tree[ls]+tree[rs]);
}
bool update(int* tree,int l,int r,int x,int i){
if(l==r){
tree[i]=1;return 1;
}else{
int mid=(l+r)/2;
if(x>mid&&r-mid!=tree[rs]){
int nm=update(tree,mid+1,r,x,rs);
if(nm){
up(i,tree);return 1;
}
}
if(mid-l+1!=tree[ls]){
int nm=update(tree,l,mid,x,ls);
up(i,tree);
return nm;
}else return 0;
}
}
void build(int* tree,int l,int r,int i){
if(l==r){
tree[i]=0;
}else{
int mid=(l+r)/2;
build(tree,l,mid,ls);
build(tree,mid+1,r,rs);
up(i,tree);
}
}
void init(int n){
build(tree1,1,n,1);
build(tree2,1,n,1);
}
void solve(){
int n;cin>>n;init(n);
for(int i=1;i<=n;i++){
cin>>v[i];
}
int l=1;int r=n;
int minn=1;
int ans=0;int L=0,R=0;
vector<int>vis(n+1);
while(l<r){
if(!vis[l]){
if(update(tree1,1,n,v[l],1))L++;
vis[l]=1;
}
if(!vis[r]){
if(update(tree2,1,n,v[r],1))R++;
vis[r]=1;
}
if(minn<=L&&minn<=R){
ans+=r-l;minn++;
}else if(minn>L)l++;
else if(minn>R)r--;
}
cout<<ans<<endl;
}
using namespace std;
signed main(){
#ifdef ONLINE_JUDGE
#else
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
ios::sync_with_stdio(false);cin.tie(0);
int T;cin>>T;
while(T--){
solve();
}
}

浙公网安备 33010602011771号