暂时只能写签到题,别的题解之后补
A.Angle Beats
B.Best Subsequence
有点有趣的二分
显然二分之后就是找到一个长度恰好为k的合法环了
先证明一个引理,如果可以找到一个长度大于k的环一定可以找到一个长度等于k的环
固定头尾不动之后就可以使用归纳法证明一个长度大于3的环都可以删掉一个点
为啥呢?,因为考虑任意三个连续的点,除非中间的点是最小值,否则都可以删掉这个点
很容易证明长度大于3的序列至少存在一组三个连续的点满足中间的点可以删去吧
而长度恰好是3的时候吧中间的点删掉就行了,因为头尾是合法的
这样的话我们的目标就是dp出最长的环了
那么dp出最长的环需要借助另一个结论优化到\(O(nlogn)\)
我们将整个序列按照这样的方式切分成若干个段
找到整个序列的最小值,然后把最小值之前的点全部分成一个段,递归切分剩下的段
这样分好段之后我们把每一段的末尾成为关键点
我们断言(证明反证),一个最长环一定可以在关键点结尾
并且当我们固定了一个环的头尾之后,最长环的必定经过头尾间的所有关键点
这样有什么用呢?
当然拿来辅助dp啊,显然最长链一个树状数组搞定
加上环的限制就是开头定死之后结尾小于一定数值了
显然扫描线可以干掉限制,依次允许一些点成为结尾,然后查询某些开头的dp值即可
我们刚才结论决定了只需要考虑所有的关键点即可,并且容易发现关键点的权值随着位置递增
这就简单多了,你会发现每个点出发的链一定会经过离自己最近的关键点,后面的转移完全一样
所以一段一段的插入dp值就行了,预处理每个点到关键点的最长链(树状数组做)
然后关键点和关键点之间的dp拿前缀和处理就行了
复杂度二分套dp\(O(nlog^2n)\)
#include
#include
using namespace std;const int N=4*1e5+10;typedef long long ll;
int n;int k;int S;
struct linetree
{
int ta[N];
inline void ins(int x,int t)
{
for(;x<=n;x+=x&(-x))ta[x]=max(ta[x],t);
}
inline int qry(int x)
{
int r=-0x3f3f3f3f;
for(;x;x-=x&(-x))r=max(r,ta[x]);
return r;
}
inline void ih()
{
for(int i=0;i=lst)
{
for(int j=lst;j<=srt[i];j++)bel[j]=srt[i];
lst=srt[i]+1;
}
rk[srt[1]]=1;
for(int i=2;i<=n;i++)
rk[srt[i]]=rk[srt[i-1]]+(a[srt[i]]!=a[srt[i-1]]);
}
int currk[N];
inline bool jud(int lim)
{
//printf("jud %d\n",lim);
ll res=-0x3f3f3f3f;
for(int i=1;i<=n;i++)dp[i]=-0x3f3f3f3f;
for(int i=1;i<=n;i++)dis[i]=0;
int lst=1;int mend=0;
lt.ih();
for(int i=n,j=0;i>=1;i--)
{
while(a[srt[j+1]]+a[srt[i]]<=lim&&j!=n)j++;
currk[srt[i]]=rk[srt[j]];
}
for(int i=n,j=1;i>=1;i--)
{
int id=srt[i];
while(a[srt[j]]+a[id]<=lim&&j<=n)
{
if(srt[j]>=lst)
{
int val=-0x3f3f3f3f;
lt.ins(rk[srt[j]],1);
dp[srt[j]]=1;
if(a[lst-1]+a[srt[j]]<=lim)val=max(val,dp[srt[j]]);
for(int z=srt[j]-1;z>=lst;z--)
if(a[z]=lst;z--)lt.d(rk[z]);
dis[srt[j]]=dis[lst-1]+val;
lst=srt[j]+1;
if(val>0)mend=lst-1;
}
j++;
}
res=max(res,dis[mend]-dis[bel[id]]+dp[id]);
if(res>=k)return true;
}
return res>=k;
}
int sum1=0;
inline int bigr(){return (rand()<<16)+rand();}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
// srand(233);
//n=200000;k=n;
//for(int i=1;i<=n;i++)
// scanf("%d",&a[i]);
// for(int i=1;i<=n;i++)
// a[i]=bigr()%(int)(1e9);
pre();
for(int i=1;i<=n;i++)
sum1=max(sum1,a[i]);
int l=2;int r=sum1*2;
// printf("%d\n",r);
while(l!=r)
{
int mid=(((ll)l+(ll)r))>>1;
if(jud(mid))r=mid;else l=mid+1;
}
printf("%d",l);
return 0;
}