合并神犇
题目背景
loidc来到了NOI的赛场上,他在那里看到了好多神犇。
题目描述
神犇们现在正排成一排在刷题。每个神犇都有一个能力值p[i]。loidc认为坐在附近的金牌爷能力参差不齐非常难受。于是loidc便想方设法对神犇们进行人道主义合并。
loidc想把神犇的能力值排列成从左到右单调不减。他每次可以选择一个神犇,把他合并到两侧相邻的神犇上。合并后的新神犇能力值是以前两位犇的能力值之和。每次合并完成后,被合并的两个神犇就会消失。合并后的新神犇不能再分开(万一他俩有女朋友咋办)因此每次合并后神犇的总数会减1.
loidc想知道,想治好他的强迫症需要合并多少次
输入输出格式
输入格式:第一行一个整数 n。
第二行 n 个整数,第 i 个整数表示 p[i]。
输出格式:loidc需要合并的次数
输入输出样例
输入样例#1:
8 1 9 9 4 1 2 2 9
输出样例#1:
3
说明
对于 50%的数据,0< n <=5000。
对于 100%的数据,0< n <=200000,0< p[i] <=2147483647,p 均为随机生成。
n^2水过
f[i]为第1~i位合并的次数
pre[i]为第1~i位最末尾的数
j为满足sum[i]-sum[j]>=pre[j]的最大数
f[i]=f[j]+i-j-1
pre[i]=sum[i]-sum[j]
显然pre[i]越小越好,这样找到一个就可以退出
正好数据很水
复杂度强迫症硬是想nlogn可以套一个平衡树
或者写一个二分
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int f[200001],n; 7 long long sum[200001],pre[200001]; 8 int main() 9 {int i,j; 10 cin>>n; 11 for (i=1;i<=n;i++) 12 { 13 scanf("%lld",&sum[i]); 14 sum[i]+=sum[i-1]; 15 } 16 for (i=1;i<=n;i++) 17 { 18 for (j=i-1;j>=0;j--) 19 { 20 if (sum[i]-sum[j]>=pre[j]) 21 break; 22 } 23 f[i]=f[j]+i-j-1; 24 pre[i]=sum[i]-sum[j]; 25 } 26 cout<<f[n]; 27 }
这里附上单调队列的代码,由USACO干草堆改来
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long lol; 7 lol sum[1000001],pre[1000001],f[1000001]; 8 int q[1000001],head,tail,n; 9 int main() 10 {int i; 11 cin>>n; 12 for (i=1;i<=n;i++) 13 { 14 scanf("%lld",&sum[i]); 15 sum[i]+=sum[i-1]; 16 } 17 head=0;tail=1; 18 q[0]=0; 19 for (i=1;i<=n;i++) 20 { 21 while (head+1<tail&&sum[i]>=sum[q[head+1]]+pre[q[head+1]]) head++; 22 f[i]=f[q[head]]+1; 23 pre[i]=sum[i]-sum[q[head]]; 24 //cout<<q[head]<<endl; 25 while (head<tail&&sum[q[tail-1]]+pre[q[tail-1]]>sum[i]+pre[i]) tail--; 26 q[tail++]=i; 27 } 28 cout<<n-f[n]; 29 }