其他分享
首页 > 其他分享> > codeforces gym 102268 300iq round

codeforces gym 102268 300iq round

作者:互联网

暂时只能写签到题,别的题解之后补

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<cstdio>
#include<algorithm>
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<N;i++)ta[i]=-0x3f3f3f3f;
    }
    inline void d(int x)
    {
        for(;x<=n;x+=x&(-x))ta[x]=-0x3f3f3f3f;
    }
 
}lt;
int a[N];int srt[N];int rk[N];
inline bool cmp(const int & x,const int & y)
{
    if(a[x]==a[y])return x<y;return a[x]<a[y];
}
int dp[N];int bel[N];ll dis[N];
inline void pre()
{
    for(int i=1;i<=n;i++)srt[i]=i;
    sort(srt+1,srt+n+1,cmp);
    int lst=1;
    for(int i=1;i<=n;i++)
        if(srt[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]<lim)
                    {
                        dp[z]=lt.qry(currk[z])+1;
                        lt.ins(rk[z],dp[z]);
                        if(a[lst-1]+a[z]<=lim)val=max(val,dp[z]);
                    }
                for(int z=srt[j];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;
}

C.Cool Pairs

D.Dates

E.Expected Value

F.Free Edges

G. Graph Counting

H. Hall's Theorem

I.Interesting Graph

J.Jealous Spilt

K.Knowledge

标签:102268,int,gym,300iq,lst,srt,dis,dp,关键点
来源: https://www.cnblogs.com/sweetphoenix/p/11172948.html