题目如下:
= =因为不会长截图所以只能这样了。
题意:
给你一排苹果的高度,每次只能摘第一个或者比上一次摘的苹果更高的苹果,问最多摘几个。(应该是强制摘,即不贪心求最优)。每组样例给你n个苹果的高度和m次询问,每次询问修改一个苹果的高度,且无后效性(这次询问不对下次询问有影响,即每次都从原队列里修改。)
分析:
T=10,n,m=1e5。暴力时间是 Tmn=1e11.爆炸。
然后,我们能接受的时间复杂度是O(T(n+m)log(n))。即所有使用二分和一次遍历的算法是可以接受的。
想了很久,最后用了拼接的算法。具体和答案类似,直接上答案:
从对原序列进行预处理着手。
发现,原序列断开,我们可以通过左右段的某些信息来拼接答案。
对于左段来说:
知道左边一格的答案(当前最大值与苹果数量)(1次遍历)
对于右段来说:
找到第一个大于此前的数(到断点为止的最大值)->(二分ST表,找最靠左的第一个大于当前最大值的数)->求从这一点开始到序列尾部的答案。(预处理遍历 t=2*n,用二分可以变成n+logn (? _?),即倒着存储用upper_bound,又PS:这里错了好多,没对upper_bound理解正确,不过最坏情况不过是多历遍一次所以不会有问题。)
对于每次询问:
• 考虑左段加上这个数之后的答案和最大值;
• 再找到右边第一个大于左半部分最大值的数,答案相加即可。
预处理使用ST 表,每次查询需要一个二分,总复杂度O(4*n+nlogn+logn)=O(nlogn)。
即除了ST表初始化,其他时间都很短。
最后上代码。
代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
//const int maxn = 1e5;
#define inf 0x3fffffff
#define ll long long
#define db double
#define s(x) scanf("%d",&x);
#define sd(x) scanf("%lld",&x);
#define sf(x) scanf("%lf",&x);
#define rep(i,m,n) for(i=m;i<=n;i++)
#define mem(a) memset(a,0,sizeof(a))
int sum[100010];//源数据
int num[100010];
int pmaxn[100010];
int pans[100010];
int sans[100010];
ll m,n;
int dp[100001][18];
void ST()
{
for (int i = 1; i <= n; i++)
dp[i][0] =sum[i];
for (ll j = 1; (1 << j) <= n; j++)
{
for (ll i = 1; i + (1 << j) - 1 <= n; i++)
{
dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
}
}
}
int findd(int l,int k,int vaule)
{
if(k==0)
return l;
k--;
if(dp[l][k]>vaule) return findd(l,k,vaule);
return findd(l+(1<<k),k,vaule);
}
int RMQ(int l, int r,int vaule)
{
//if(l==r)return sum[l];
int k = 0,a,b;
if(l>r) return -1;
if(l==r)
{
if(sum[l]>vaule)return l;
else return -1;
}
while ((1 << (k + 1)) <= r - l + 1) k++;
//cout<<dp[l][k]<<endl;
//cout<<dp[r - (1 << k) + 1][k]<<endl;
a=dp[l][k];
if(a>vaule) return findd(l,k,vaule);
b=dp[r - (1 << k) + 1][k];
if(b>vaule) return findd(r - (1 << k) + 1,k,vaule);
else
return -1;
}
void init()
{
int k,a,i,j;
pmaxn[1]=sum[1];
pans[1]=1;
a=1;
for(i=2; i<=n; i++)//前缀答案
{
if(pmaxn[i-1]>=sum[i])
{
pmaxn[i]=pmaxn[i-1];
pans[i]=a;
}
else
{
pmaxn[i]=sum[i];
pans[i]=++a;
}
}
a=1;
num[0]=sum[n];
sans[n]=1;
for(i=n-1; i>0; i--) //尾缀答案
{
if(num[0]<=sum[i])
{
sans[i]=1;
num[0]=sum[i];
a=1;
}
else
{
for(j=a-1;j>=0;j--)
if(num[j]>sum[i])break;
j++;
num[j]=sum[i];
a=j+1;
sans[i]=a;
/*k=0;
k=upper_bound(num,num+a,sum[i])-num;
k+=1;
num[k]=sum[i];
k+=1;
a=k;
sans[i]=a;*/
}
}
/*for(i=1;i<=n;i++)cout<<sans[i]<<" ";
cout<<endl<<endl;*/
//rmq
ST();
}
void solve(int index)
{
int k,a,b;
if(index==1)
{
k=RMQ(2,n,sum[1]);
if(k==-1) cout<<1<<endl;
else //cout<<sum[k]<<" "<<k<<" ",
cout<<1+sans[k]<<endl;
}
else if(index==n)
{
k=pans[n-1];
if(sum[index]>pmaxn[n-1])
k++;
cout<<k<<endl;
}
else
{
a=pans[index-1];
if(pmaxn[index-1]>=sum[index])
{
b=pmaxn[index-1];
}
else
{
b=sum[index];
a++;
}
k=RMQ(index+1,n,b);
if(k==-1)cout<<a<<endl;
else cout<<a+sans[k]<<endl;
}
}
int main()
{
#ifdef _zxdin_
freopen("C:\\Users\\A\\Desktop\\data.in","r",stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int t,i,j,k,a,b,c;
cin>>t;
while(t--)
{
cin>>n>>m;
rep(i,1,n) cin>>sum[i];
if(n==1)
{
while(m--)
{
cin>>a>>b;
cout<<1<<endl;
}
continue;
}
init();
/* for(i=1;i<=n;i++)cout<<sans[i]<<" ";
cout<<endl<<endl;*/
// cout<<"st: "<<n<<endl;
rep(i,1,m)
{
//cout<<i<<": "<<endl;
cin>>a>>b;
c=sum[a];
sum[a]=b;
//cout<<"ss: "<<n<<endl;
solve(a);
sum[a]=c;
//cout<<"end: "<<n<<endl;
}
}
return 0;
}
末尾语:
这是我的第一篇博文,代码里有很多事debug的段落被注释掉了但我不会删。写博客是因为回顾与记录,希望能从这道题里学到算法和错漏。以上。