zoukankan      html  css  js  c++  java
  • 【JOISC2019|2019】【20190622】cake3

    题目

    (N) 个物品中选(M)个,排列成一个环:(k_1,cdots,k_M)价值为:

    [sum_{j=1}^{N}{V_i} - sum_{j=1}^{M}|C_{k_j}- C_{k_{j\%M+1} }| ]

    $3 le N,M le 2 imes 10^5 $

    题解

    • 对于一个(k)(C)最小的排列的贡献是(2 ( max - min))

    • 因为将 $ k $ 按 $ C $ 排序,由于最终是一个环, $ C_{k_j} - C_{k_{j+1}} $ 一定会被至少经过两次

    • 将物品按(C)排序,枚举最大点,枚举最小点,中间选(V)最大的

    • 可以发现从左向右枚举右端点,最小值的选择是单调的

    • 但是单调可能是不连续的,接下来有一个套路:

    • 计算([l,r])的值时,可以先找(mid = (l + r) /2) 的最优决策点(pos_{mid})

    • 那么(pos_{[l,mid-1]} le pos_{mid} , pos_{[mid+1,r]} ge pos_{mid}) ,分治求下去

    • 需要查找区间前(m-2)大的和,如果用主席树实现

    • 时间复杂度:(O(N log^2 N ))

      #include<bits/stdc++.h>
      #define ll long long 
      #define inf 1e18
      
      using namespace std;
      
      const int N=200010,M=20;
      int n,m,st[N],tp,sub[N],tot,sz,cnt[N*M],ls[N*M],rs[N*M],rt[N];
      ll sum[N*M],a[N],b[N],ans=-inf;
      struct data{int x,y;}A[N];
      bool operator <(data X,data Y){return X.y<Y.y;}
      
      void ins(int&k,int lst,int l,int r,int x,int y){
      	sum[k=++sz]=sum[lst]+y;cnt[k]=cnt[lst]+1;
      	ls[k]=ls[lst],rs[k]=rs[lst];
      	if(l==r)return;
      	int mid=(l+r)>>1;
      	if(x<=mid)ins(ls[k],ls[lst],l,mid,x,y);
      	else ins(rs[k],rs[lst],mid+1,r,x,y);
      }
      
      ll query(int k,int lst,int l,int r,int x){
      	if(l==r||!x)return 1ll*sub[l]*x;
      	int mid=(l+r)>>1,tmp=cnt[rs[k]]-cnt[rs[lst]];
      	if(x<tmp)return query(rs[k],rs[lst],mid+1,r,x);
      	return sum[rs[k]]-sum[rs[lst]]+query(ls[k],ls[lst],l,mid,x-tmp);
      }
      
      ll cal(int l,int r){
      	if(r-l+1<m)return -inf;
      	return a[l]+b[r]+query(rt[r-1],rt[l],1,tot,m-2);
      }
      
      void solve(int L,int R,int l,int r){
      	if(L>R)return;
      	if(l==r){for(int i=L;i<=R;++i)ans=max(ans,cal(st[l],i));}
      	int Mid=(L+R)>>1,mid=l;ll tmp=-inf;
      	for(int i=l;i<=r;++i){
      		ll now=cal(st[i],Mid);
      		if(tmp<now)tmp=now,mid=i;
      	}
      	ans=max(ans,tmp);
      	solve(L,Mid-1,l,mid);
      	solve(Mid+1,R,mid,r);
      }
      
      int main(){
      	freopen("cake3.in","r",stdin);
      	freopen("cake3.out","w",stdout);
      	
      	scanf("%d%d",&n,&m);
      	for(int i=1;i<=n;++i){
      		scanf("%d%d",&A[i].x,&A[i].y);
      		sub[++tot]=A[i].x;
      	}
      	
      	sort(sub+1,sub+tot+1);
      	tot=unique(sub+1,sub+tot+1)-sub-1;
      	sort(A+1,A+n+1);
      
      	for(int i=1;i<=n;++i){
      		int pos=lower_bound(sub+1,sub+tot+1,A[i].x)-sub;
      		ins(rt[i],rt[i-1],1,tot,pos,A[i].x);
      		
      		a[i]=A[i].x+2ll*A[i].y;
      		b[i]=A[i].x-2ll*A[i].y;
      		if(!tp||a[i]>a[st[tp]])st[++tp]=i;
      	}
      	solve(m,n,1,tp);
      	/*ll ans=cal(1,m);
      	for(int i=m,j=1;i<=n;++i){
      		while(j<tp&&st[j+1]+m-1<=i&&cal(st[j],i)<cal(st[j+1],i))j++;
      		ans=max(ans,cal(st[j],i));
      		for(int k=1;k<=tp&&st[k]+m-1<=i;++k){
      			printf("%lld ",cal(st[k],i));
      		}
      		puts("");
      	}*/
      	//这段是不对的,因为并不是所有单调都是连续的
      	//像斜率优化的那种可以用单调队列维护的只是单调的一种特殊情况
      	//不单调可以用分治做
      	//20190623
      	cout<<ans<<endl;
      	return 0;
      }
      
      
  • 相关阅读:
    BZOJ1443 [JSOI2009]游戏Game
    BZOJ4950 [Wf2017]Mission Improbable
    假期编程
    假期编程
    假期编程
    假期编程
    假期编程练习-求和
    假期编程练习——一个数的n次幂取余
    假期编程练习———十进制转二进制
    小球抛物线运动
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/11073929.html
Copyright © 2011-2022 走看看