zoukankan      html  css  js  c++  java
  • CF1477E

    CF1477E - Nezzar and Tournaments

    题目大意

    有两队人(a_i,iin[1,n],b_j,jin[1,m]),现在把他们放在一起排成一行(c_i)

    顺次给每个人计分,初始(s_0=k)

    (s_i=max{0,s_{i-1}+c_i-c_{max{i-1,1}}})

    现在要最大化每个(a_i)所在位置的(s_i)之和 与 (b_i)所在(s_i)之和 的差

    支持修改和对于不同(k)查询


    分析

    考虑(k=0)简单情况

    1.若(s_i)不清零,则(s_i=c_i-lst),其中(lst)表示上一个被清零位置的(c_j)

    2.(s_i)清零,则(c_i<lst)

    容易发现,(displaystyle s_i=c_i-min_{jleq i}{ c_j})


    那么对于含(k)的情况,类似可以得到

    (displaystyle s_i=k-c_1+c_i+max{0,c_1-k-min_{jleq i}{c_j}})

    假设我们固定了一个(c_1),现在考虑对于剩下的(a_i,b_j)排出一个最优的排列

    容易发现,(k-c_1+c_i)的贡献时固定的,只有前缀最小值会影响答案

    我们希望对于(b_i),前缀最小值较大,(a_i)反之

    那么容易发现可以先降序排列(b_j),再正序排列(a_i)

    此时(b_{min})可以贡献给(a_i)的前缀最小值,同时(b_j)的前缀最小值能够取到最大


    此时,不妨设(c_1=t)(min{a_i,b_i}=Min)

    (min{c_j}=c_1)时,(max)里的东西没有贡献,故可以得到

    1.对于每个(a_i),若它没有被放在(c_1),则贡献(k-t+a_i+max{0,t-k-Min})

    2.对于每个(b_i)(不特殊考虑第一个),则贡献(-(k-t+b_i+max{0,t-k-b_i}))(忽略最小值为(t)的情况)

    则最终式子为

    (displaystyle f(t)=(n-[tin a_i])cdot max{0,t-k-Min}-sum max{0,t-k-b_i}+(m-n)t+C)

    其中(C=(n-m)k+sum a_i-sum b_i)

    容易发现(f(t))是关于(t)的分段一次函数,根据斜率变化情况分析,极值位置仅(O(1))

    那么对于(a_i)作为(t)(b_j)作为(t)的情况,分别计算(f(t))的极值位置

    极值位置需要一个(k)大查询和( ext{lower_bound})

    计算(f(t))需要一个前缀查询

    我用( ext{BIT})充当平衡树来维护,复杂度为(O((n+m+q)log 10^6))

    const int N=1e6+10,INF=1e9+10;
    
    int n,m,q;
    int a[N],b[N];
    struct BIT{
    	ll s[N];
    	int c[N],n;
    	void Init(int m){ n=m; }
    	void Add(int p,int x,int y){
    		p++;
    		while(p<N) s[p]+=x,c[p]+=y,p+=p&-p;
    	}
    	ll Que(int p){
    		p++;
    		if(p<=0) return 0;
    		ll sum=0,cnt=0,t=p-1;
    		while(p) sum+=s[p],cnt+=c[p],p-=p&-p;
    		return t*cnt-sum;
    	}
    	int Rank(int p){
    		if(p<0) return 0; // 一些奇怪的边界特判 ,防止查询越界
    		p++,cmin(p,N-1);
    		int res=0;
    		while(p) res+=c[p],p-=p&-p;
    		return res;
    	}
    	int Kth(int k){ // 注意一定要避免找到并不存在的数值
    		cmin(k,n),cmax(k,1);
    		int p=0;
    		drep(i,19,0) if(p+(1<<i)<N && c[p+(1<<i)]<k) k-=c[p+=1<<i];
    		return p;
    	}
    	int Prev(int x) { return Kth(Rank(x)); }
    	int Next(int x) { return Kth(min(n,Rank(x)+1)); }
    } A,B;
    
    ll delta;
    void AddA(int x,int k){
    	delta+=x*k;
    	A.Add(x,x*k,k);
    }
    void AddB(int x,int k){
    	delta-=x*k;
    	B.Add(x,x*k,k);
    }
    
    ll QueA(ll k){
    	ll Min=min(A.Kth(1),B.Kth(1));
    	auto F=[&](ll t){ return (n-1)*max(0ll,t-k-Min)-B.Que(t-k)+(m-n)*t; };
    	ll ans=max(F(A.Kth(1)),F(A.Kth(n)));
    	int p=B.Kth(m-1)+k;
    	cmax(ans,F(A.Prev(p))),cmax(ans,F(A.Next(p)));
    	return ans;
    }
    
    ll QueB(ll k){
    	ll Min=min(A.Kth(1),B.Kth(1));
    	auto F=[&](ll t){ return n*max(0ll,t-k-Min)-B.Que(t-k)+(m-n)*t; };
    	ll ans=max(F(B.Kth(1)),F(B.Kth(m)));
    	int p=B.Kth(m)+k;
    	cmax(ans,F(B.Prev(p))),cmax(ans,F(B.Next(p)));
    	return ans;
    }
    
    ll Que(ll k){
    	return max(QueA(k),QueB(k))+delta+(n-m)*k;
    }
    
    int main(){
    	n=rd(),m=rd(),q=rd(),A.Init(n),B.Init(m);
    	rep(i,1,n) AddA(a[i]=rd(),1);
    	rep(i,1,m) AddB(b[i]=rd(),1);
    	while(q--) {
    		int opt=rd();
    		if(opt==1) {
    			int x=rd(),y=rd();
    			AddA(a[x],-1),AddA(a[x]=y,1);
    		} else if(opt==2) {
    			int x=rd(),y=rd();
    			AddB(b[x],-1),AddB(b[x]=y,1);
    		} else printf("%lld
    ",Que(rd()));
    	}
    }
    
  • 相关阅读:
    linux常用统计命令
    linux文件处理命令
    linux三剑客和管道使用
    bash编程语法
    第八章:用通配符进行过滤
    第七章:数据过滤
    第六章:过滤数据
    第五章:排序检索数据
    第四章:检索数据
    第二章:MYSQL简介
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14793167.html
Copyright © 2011-2022 走看看