瓜分领土(线段树)

石头、剪刀和布闹别扭了,他们要分家。
他们生活在一个离散的一维空间里,简单点说,他们拥有在一条直线上的N间房子,每间房子有一个风水值(有正有负)。
然后,他们决定将这N间房子分成非空的三个连续段,从左到右数,第一段的房子全部属于石头,第二段的房子全部属于剪刀,第三段的房子全部属于布。
由于他们希望公平,并且又由于剪刀是他们的老大哥,他们决定根据这些条件制定了一个评判标准:
设石头拥有的房子的风水值和为a,剪刀拥有的房子的风水值和为b,布拥有的房子的风水值和为c,剪刀拥有n间房子。
那么通过给定一个参数x。
那么,这种分配的合理值就是max(a,b,c)-min(a,b,c)+x*n.
合理值越小,表示这种分配越合理。
因此,我们现在就是要求出这个最小的合理值。

对于30%的数据,N<=10.
对于70%的数据,N<=1000.
对于100%的数据,N<=100000,保证所有运算结果在long long范围内。

输入格式 

第一行一个正整数N。
第二行有N个整数,表示房子的风水值,按从左到右的顺序给出。
第三行一个整数x。

输出格式 

一行一个整数,表示最小的合理值。

输入样例 

4
1 1 1 1
-1

输出样例 

-1

题解:

看到有max和min,感觉有点烦,不如我们强行把顺序定下,就会出现6种情况,这里只选a>b>c来说。我们规定i为石头房屋的结尾,j为剪刀房屋的结尾。

当a>b>c时,即sum[i]>sum[j]-sum[i]>sum[n]-sum[j],我们把这个式子整理可得:

2*sum[i]>sum[j]   sum[i]<2*sum[j]-sum[n]  i<j

所以当我们按前缀和sum排好序后,枚举j的时候,属于该情况下可行的i一定是连续的一段,于是我们可以二分找出i的范围。

 

当a>b>c时,合理值即为:sum[i]-(sum[n]-sum[j])+x*(j-i) = sum[j]+xj-sum[n]-xi+sum[i]-xi;

因为我们枚举的是j,对于每一个j,sum[j]+xj-sum[n]是固定的,我们要求合理的i中sum[i]-xi的最小值,又因为合理的j按sum[j]排序后是连续一段的,所以我们考虑用线段树维护。

 

当a>b>c时,我们开一棵线段树,以sum[i]为关键字,插入sum[i]-xi,并且维护最小值。

每当我们枚举一个j时,把j-1的信息相应的存入线段树中。别忘了还有其他五种情况,再次不一一列举。

 

所以我们枚举j要n次,插入j-1也是n次,然后二分要logn,查询要logn,总的复杂度大概O(nlogn*6)。不虚!

#include<algorithm>
#include<fstream>
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,ds[100010],lg;
long long x,p[100010];
struct tedge
{
 long long he;
 int x;
}sum[100010];
long long tree[800010][7],ans;

bool cmp(tedge a,tedge b)
{
 return a.he<b.he;
}

void Updata(int zu,int root,int l,int r,int x,long long shu)
{
 if (l==r&&r==x) 
 {
  tree[root][zu] = shu;
  return;
 }
 int mid = (l+r)/2;
 if (x<=mid) Updata(zu,root*2,l,mid,x,shu);
 else Updata(zu,root*2+1,mid+1,r,x,shu);
 tree[root][zu] = min(tree[root*2][zu],tree[root*2+1][zu]);
 return;
}

long long Query(int zu,int root,int l,int r,int cl,int cr)
{
 if (cl<=l&&r<=cr) return tree[root][zu];
 if (cl>r||cr<l) return 1e18;
 int mid=(l+r)/2;
 return min(Query(zu,root*2,l,mid,cl,cr),Query(zu,root*2+1,mid+1,r,cl,cr));
}
void twofen(long long h,long long t,int zu,int i)
{
 int l=0,r=n+1,ph,pt; 
 while (l+1<r)
 {
  int mid = (l+r)/2;
  if (sum[mid].he>=h) r = mid;
  else l = mid;
 }

 ph = r;
 l=0; r=n+1;
 while (l+1<r)
 {
  int mid = (l+r)/2;
  if (sum[mid].he<=t) l = mid;
  else r = mid;
 }

 pt = l;
 if (ph>pt) return;
 long long counter;
 if (zu==1) counter = sum[ds[n]].he-sum[ds[i]].he-x*i+Query(zu,1,1,n,ph,pt);
 else if (zu==2)  counter = Query(zu,1,1,n,ph,pt)-2*sum[ds[i]].he-x*i;
 else if (zu==3)  counter = Query(zu,1,1,n,ph,pt)+2*sum[ds[i]].he-x*i;
 else if (zu==4)  counter = Query(zu,1,1,n,ph,pt)+sum[ds[n]].he+sum[ds[i]].he-x*i;
 else if (zu==5)  counter = Query(zu,1,1,n,ph,pt)-sum[ds[n]].he-sum[ds[i]].he-x*i;
 else if (zu==6)  counter = Query(zu,1,1,n,ph,pt)-sum[ds[n]].he+sum[ds[i]].he-x*i;
 ans = min(ans,counter);
}
void erfen(int x,int i)
{
 long long h,t;
 if (x==1) {h=2*sum[ds[i]].he; t=(sum[ds[n]].he+sum[ds[i]].he)/2; }
 else if (x==2) {h=(sum[ds[n]].he+sum[ds[i]].he+1)/2; t=sum[ds[n]].he-sum[ds[i]].he; }
 else if (x==3) {h=sum[ds[n]].he-sum[ds[i]].he;  t=(sum[ds[n]].he+sum[ds[i]].he)/2;  }
 else if (x==4) {t=min(2*sum[ds[i]].he,sum[ds[n]].he-sum[ds[i]].he); h=-1e17;}
 else if (x==5) {h=max(2*sum[ds[i]].he,sum[ds[n]].he-sum[ds[i]].he); t=1e18;}
 else if (x==6) {h=(sum[ds[n]].he+sum[ds[i]].he+1)/2; t=2*sum[ds[i]].he;}
 twofen(h,t,x,i);
}
int main()
{
 freopen("2070.in","r",stdin);
 freopen("2070.out","w",stdout);
 scanf("%d",&n);
 for (int i=1; i<=n; i++)
 scanf("%lld",&p[i]);
 scanf("%lld",&x);
 for (int i=1; i<=n; i++)
 sum[i].he = sum[i-1].he+p[i];
 for (int i=1; i<=n; i++)
 sum[i].x = i;
 sort(sum+1,sum+1+n,cmp);
 for (int i=1; i<=n; i++)
 ds[sum[i].x] = i;
 
 lg = 1;
 while (lg<n) lg = lg*2;
 for (int i=1; i<=lg*2; i++)
   for (int j=1; j<=6; j++)
   tree[i][j] = 1e17;
 ans = 1e17;
 for (int i=n-2; i>=1; i--)
 {
  int j = i+1;
  Updata(1,1,1,n,ds[j],x*j-sum[ds[j]].he);//abc 
  Updata(2,1,1,n,ds[j],sum[ds[j]].he+x*j);//acb
  Updata(3,1,1,n,ds[j],x*j-sum[ds[j]].he);//bca
  Updata(4,1,1,n,ds[j],x*j-2*sum[ds[j]].he);//bac 
  Updata(5,1,1,n,ds[j],x*j+2*sum[ds[j]].he);//cab
  Updata(6,1,1,n,ds[j],sum[ds[j]].he+x*j);//cba
  for (int k=1; k<=6; k++)  
  erfen(k,i);
 }
 printf("%lld\n",ans);
 return 0;
}

不过这道题的推理这的很烦人,写起来也有点烦!

posted @ 2017-09-28 13:11  最终惊吓者——Janous  阅读(325)  评论(0编辑  收藏  举报