1. 程式人生 > >瓜分領土(線段樹)

瓜分領土(線段樹)

log freopen 表示 struct edge 房子 name 連續 shu

石頭、剪刀和布鬧別扭了,他們要分家。
他們生活在一個離散的一維空間裏,簡單點說,他們擁有在一條直線上的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;
}

不過這道題的推理這的很煩人,寫起來也有點煩!

瓜分領土(線段樹)