• UOJ#749[UNR #6]稳健型选手【贪心,分治,主席树】


    正题

    题目链接:https://uoj.ac/problem/749


    题目大意

    如果有序列\(a\),你每次取走一个数字后然后这个序列最前面的数字会被别人取走,直到序列为空。此时\(f(a)\)表示你最大能取走的权值和。

    给出一个长度为\(n\)的序列\(a\)\(q\)次询问区间\([l,r]\),求\(f(a_{l\sim r})\)

    \(1\leq n,q\leq 2\times 10^5,1\leq a_i\leq 10^9\)


    解题思路

    考虑一下最优的取法,我们的限制其实相当于前\(2i-1\)个数之中不能取走超过\(i\)个数。

    一个暴力的贪心想法是不停往前走,用一个堆维护现在取了的数,走到不是\(2i-1\)时我们考虑是否拿这个\(a_i\)替代堆里最小的数,走到\(2i-1\)时直接丢进堆里就行了。

    或者反过来更简单,走到一个数就丢进堆里,遇到\(2i-1\)时直接取堆中最大的数就好了。

    我们现在能处理左或右端点固定的所有答案了,我们考虑分治,问题在于怎么去合并两个区间的答案。

    为了防止大量的分类讨论,对于长度为奇数的区间询问,我们可以考虑去掉区间的末尾,因为这个位置肯定会取到。

    然后我们考虑怎么合并区间,显然是前面一些选了的数字变成不选,后面一些不选的数字变成选了,显然是前面拿小的,后面拿大的。

    我们考虑在分治的时候对于区间\([L,mid,R]\),对于\([L,mid]\)部分,我们用主席树维护选了的数字,对于\([mid+1,R]\)部分,我们用主席树维护没选的数字,然后对于一个询问,二分一个\(k\),然后左边的取出前\(k\)小,右边的取出前\(k\)大进行比较即可。

    时间复杂度:\(O(n\log^2 n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define ll long long
    using namespace std;
    const ll N=2e5+10,M=N<<5;
    ll n,m,cnt,a[N],b[N],l[N],r[N],ans[N],s[N],rt[N];
    priority_queue<ll> q;
    struct SegTree{
    	ll cnt,w[M],c[M],ls[M],rs[M];
    	ll Change(ll x,ll L,ll R,ll pos,ll val){
    		ll p=++cnt;w[p]=w[x]+val;c[p]=c[x]+val*b[pos];
    		if(L==R)return p;ll mid=(L+R)>>1;
    		if(pos<=mid)ls[p]=Change(ls[x],L,mid,pos,val),rs[p]=rs[x];
    		else rs[p]=Change(rs[x],mid+1,R,pos,val),ls[p]=ls[x];
    		return p;
    	}
    	ll Find(ll x,ll L,ll R,ll k){
    		 if(L==R)return L;ll mid=(L+R)>>1;
    		 if(w[ls[x]]>=k)return Find(ls[x],L,mid,k);
    		 return Find(rs[x],mid+1,R,k-w[ls[x]]);
    	}
    	ll Ask(ll x,ll L,ll R,ll k){
    		if(!k)return 0;
    		if(L==R)return b[L]*k;ll mid=(L+R)>>1;
    		if(w[ls[x]]>=k)return Ask(ls[x],L,mid,k);
    		 return Ask(rs[x],mid+1,R,k-w[ls[x]])+c[ls[x]];
    	}
    }T;
    bool cmp(ll x,ll y){return l[x]<l[y];}
    void solve(ll L,ll R,vector<ll> &now){
    	if(L==R)return;
    	ll mid=(L+R)>>1;
    	vector<ll> pl,pr,p;
    	for(ll i=0;i<now.size();i++)
    		if(l[now[i]]<=mid&&r[now[i]]>mid)p.push_back(now[i]);
    		else if(l[now[i]]<=mid)pl.push_back(now[i]);
    		else pr.push_back(now[i]);
    	solve(L,mid,pl);solve(mid+1,R,pr);
    	if(!p.size())return;
    	for(ll g=0;g<2;g++){
    		T.cnt=rt[mid]=s[mid]=0;
    		for(ll i=mid+1;i<=R;i++){
    			if(((i-mid)&1)==g)q.push(-a[i]),s[i]=s[i-1]+b[a[i]],rt[i]=rt[i-1];
    			else{
    				if(q.size()&&a[i]>-q.top()){
    					s[i]=s[i-1]-b[-q.top()]+b[a[i]];
    					rt[i]=T.Change(rt[i-1],1,cnt,-q.top(),1);
    					q.pop();q.push(-a[i]);
    				}
    				else rt[i]=T.Change(rt[i-1],1,cnt,a[i],1),s[i]=s[i-1];
    			}
    		}
    		while(!q.empty())q.pop();
    		for(ll i=mid;i>=L;i--){
    			q.push(a[i]);
    			if(((mid-i)&1)==g){
    				rt[i]=T.Change((i==mid)?0:rt[i+1],1,cnt,q.top(),1);
    				s[i]=((i!=mid)?s[i+1]:0)+b[q.top()];q.pop();
    			}
    			else if(i!=mid)rt[i]=rt[i+1],s[i]=s[i+1];
    		}
    		while(!q.empty())q.pop();
    		for(ll i=0;i<p.size();i++){
    			if(((mid-l[p[i]])&1)!=g)continue;
    			ll X=T.w[rt[l[p[i]]]],Y=T.w[rt[r[p[i]]]];
    			ll _l=1,_r=min(X,Y);
    			if(p[i]==2)
    				i++,i--;
    			while(_l<=_r){
    				ll _mid=(_l+_r)>>1;
    				if(T.Find(rt[l[p[i]]],1,cnt,_mid)<T.Find(rt[r[p[i]]],1,cnt,Y-_mid+1))
    					_l=_mid+1;
    				else _r=_mid-1;
    			}
    			ans[p[i]]+=s[l[p[i]]]-T.Ask(rt[l[p[i]]],1,cnt,_r);
    			ans[p[i]]+=s[r[p[i]]]+T.Ask(rt[r[p[i]]],1,cnt,Y)-T.Ask(rt[r[p[i]]],1,cnt,Y-_r);
    		}
    	}
    	return;
    }
    signed main()
    {
    	scanf("%lld%lld",&n,&m);
    	for(ll i=1;i<=n;i++)
    		scanf("%lld",&a[i]),b[i]=a[i];
    	sort(b+1,b+1+n);cnt=unique(b+1,b+1+n)-b-1;
    	for(ll i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
    	vector<ll> v;
    	for(ll i=1;i<=m;i++){
    		scanf("%lld%lld",&l[i],&r[i]);v.push_back(i);
    		if((r[i]-l[i]+1)&1)ans[i]+=b[a[r[i]]],r[i]--;
    	}
    	sort(v.begin(),v.end());
    	solve(1,n,v);
    	for(ll i=1;i<=m;i++)printf("%lld\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    爬弹幕
    写了这么多行就给我30,呜呜呜
    ticket
    yield求平均数
    协程原理
    爬取一类字二类字的信息和笔顺gif图片
    关于CRF的相关阅读
    embedding size与vocabulary size之间的关系: e = v**0.25
    pandas多个值取数
    转 pandas pivot
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16564143.html
Copyright © 2020-2023  润新知