test20181005 序列

Posted autoint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了test20181005 序列相关的知识,希望对你有一定的参考价值。

题意

技术分享图片
技术分享图片

考场30分

维护差值,考虑每次移动的变更,当前2-n位置上的差加1,1位置上的差减n-1。

然后要求的是绝对值的和,用吉司机线段树维护最大最小值、次大次小值。

期望复杂度(O(n log n))

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    char ch=nc();int sum=0;
    while(!(ch>=‘0‘&&ch<=‘9‘))ch=nc();
    while(ch>=‘0‘&&ch<=‘9‘)sum=sum*10+ch-48,ch=nc();
    return sum;
}
typedef long long ll;
const int INF=0x7fffffff;

const int MAXN=2e6+7;
int n;

int ql,qr,v;
struct SegTree
{
    ll sumv[MAXN<<2];
    int minv[MAXN<<2],semi[MAXN<<2],numi[MAXN<<2]; // edit 2:long long 
    int maxv[MAXN<<2],semx[MAXN<<2],numx[MAXN<<2];
    int addv[MAXN<<2];
#define lson (now<<1)
#define rson (now<<1|1)
    inline void pushup(int now)
    {
        sumv[now]=sumv[lson]+sumv[rson];
        
        minv[now]=min(minv[lson],minv[rson]);
        if(minv[lson]==minv[rson])
        {
            numi[now]=numi[lson]+numi[rson];
        }
        else
        {
            numi[now]=minv[lson]<minv[rson]?numi[lson]:numi[rson];
        }
        semi[now]=min(semi[lson],semi[rson]);
        semi[now]=min(semi[now],max(minv[lson],minv[rson]));
        
        maxv[now]=max(maxv[lson],maxv[rson]);
        if(maxv[lson]==maxv[rson])
        {
            numx[now]=numx[lson]+numx[rson];
        }
        else
        {
            numx[now]=maxv[lson]>maxv[rson]?numx[lson]:numx[rson];
        }
        semx[now]=max(semx[lson],semx[rson]);
        semx[now]=max(semx[now],min(maxv[lson],maxv[rson]));
    }
    
    void build(int now,int l,int r)
    {
        addv[now]=0;
        if(l==r)
        {
            sumv[now]=read();
            sumv[now]-=l;
            minv[now]=maxv[now]=sumv[now];
            numx[now]=numi[now]=1;
            semx[now]=0,semi[now]=n+1; // edit 3
            return;
        }
        int mid=(l+r)>>1;
        build(lson,l,mid);
        build(rson,mid+1,r);
        pushup(now);
//      cerr<<"semx "<<l<<" -> "<<r<<" ="<<semx[now]<<endl;
    }
    
    inline void pushdown(int now,int l,int r)
    {
        if(addv[now])
        {
            int mid=(l+r)>>1;
            
            sumv[lson]+=(ll)addv[now]*(mid-l+1);
            minv[lson]+=addv[now],semi[lson]+=addv[now];
            maxv[lson]+=addv[now],semx[lson]+=addv[now];
            addv[lson]+=addv[now];
            
            sumv[rson]+=(ll)addv[now]*(r-mid);
            minv[rson]+=addv[now],semi[rson]+=addv[now];
            maxv[rson]+=addv[now],semx[rson]+=addv[now]; // edit 1
            addv[rson]+=addv[now];
            
            addv[now]=0;
        }
    }
    
    void add(int now,int l,int r)
    {
        if(ql<=l&&r<=qr)
        {
            sumv[now]+=(ll)(r-l+1)*v;
            minv[now]+=v,semi[now]+=v;
            maxv[now]+=v,semx[now]+=v;
            addv[now]+=v;
            return;
        }
        pushdown(now,l,r);
        int mid=(l+r)>>1;
        if(ql<=mid)
            add(lson,l,mid);
        if(qr>=mid+1)
            add(rson,mid+1,r);
        pushup(now);
    }
    
    ll qsum(int now,int l,int r)
    {
        if(ql<=l&&r<=qr)
        {
//          cerr<<l<<" -> "<<r<<" min="<<minv[now]<<" max="<<maxv[now]<<" semx="<<semx[now]<<" numx="<<numx[now]<<" sum="<<sumv[now]<<endl;
            if(minv[now]>=0)
            {
                return sumv[now];
            }
            if(maxv[now]<=0)
            {
                return -sumv[now];
            }
            if(minv[now]<0&&semi[now]>=0)
            {
                return sumv[now]-2LL*minv[now]*numi[now];
            }
            if(maxv[now]>=0&&semx[now]<0)
            {
                return -sumv[now]+2LL*maxv[now]*numx[now];
            }
        }
        pushdown(now,l,r);
        int mid=(l+r)>>1;
        ll ans=0;
        if(ql<=mid)
        {
//          cerr<<l<<" -> "<<r<<" lsum="<<qsum(lson,l,mid)<<endl;
            ans+=qsum(lson,l,mid);
        }
        if(qr>=mid+1)
        {
//          cerr<<l<<" -> "<<r<<" rsum="<<qsum(rson,mid+1,r)<<endl;
            ans+=qsum(rson,mid+1,r);
        }
//      cerr<<l<<" -> "<<r<<" sum="<<ans<<endl;
        return ans;
    }
}T;

void debug(int n)
{
    for(int i=1;i<=n;++i)
    {
        ql=qr=i;
        printf("%lld ",T.qsum(1,1,n));
    }
    ql=1,qr=n;
    printf("
%lld
",T.qsum(1,1,n));
}

int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
    n=read();
    T.build(1,1,n);
    ql=1,qr=n;
    ll ans=T.qsum(1,1,n);
//  cerr<<"ans="<<ans<<endl;
//  debug(n);
    int p=1;
    for(int i=1;i<n;++i)
    {
        ql=1,qr=n,v=1;
        T.add(1,1,n);
        ql=p,qr=p,v=-n;
        T.add(1,1,n);
        ++p;
        ql=1,qr=n;
        ans=min(ans,T.qsum(1,1,n));
//      cerr<<"ans="<<T.qsum(1,1,n)<<endl;
//      debug(n);
    }
    printf("%lld
",ans);
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}

然而并没有我想要的60分,1e5的数据我的程序要跑30s。
算了一下程序应该是(O(n^{1.7}))的,网上的(O(n log^2 n))都是以讹传讹。

标解

技术分享图片

解释一下题解所说的。

ans前两段的分类依据是过了符号点和过了起点。

线段树的做法是在维护ans序列。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#define M 6333333
#define rg register
#define LL long long
#define _min(a,b) ((a)<(b)?(a):(b))
#define open(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)

char temp[1<<26],*S=temp,*T=temp;
char get()
{
    if(S == T)T+=fread(temp,1,1<<26,stdin);
    return *S++;
}
void re(rg int& x)
{
    rg char ch=get();x=0;
    while(ch<48)ch=get();
    while(47<ch)x=(x<<3)+(x<<1)+ch-48,ch=get();
}
using namespace std;
int n,big,sma,a[M],b[M];
LL ans,now;
int main()
{
    open(a);
    re(n);
    for(int i=1;i<=n;++i)
    {
        re(a[i]);
        int x=a[i]-i;
        if(x >= 0)++big,now+=x;
        else ++sma,now+=-x,++b[-x]; // b=cnt
    }
    ans=now;
    for(int i=1;i<n;++i) // cal ansi
    {
        int x=a[i]-1,y=a[i]-n;
        --big,now-=x;
        ++sma,now+=-y,++b[-y+i];
        now+=big-sma+1;
        if(b[i])big+=b[i],sma-=b[i];
        ans=_min(ans,now);
    }
    printf("%lld
",ans);
    return 0;
}

然而我没看懂这std写的是什么。

学长高招

考虑维护差值的数轴,数轴上存个数。

每一次操作原点向左移1个单位,ans的变化与正半轴、原点和负半轴上的差值的个数有关。
对s1变到sn的情况特殊处理就行了。

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
    T data=0;
    int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==‘-‘)
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=10*data+ch-‘0‘,ch=getchar();
    return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;

const int MAXN=2e6+7;
int n,org,L,R;
ll ans,sum;
int a[MAXN],cnt[MAXN<<2];

int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
    read(n);
    org=n<<1;
    for(int i=1;i<=n;++i)
    {
        read(a[i]);
        cnt[org+a[i]-i]++;
        if(a[i]-i<0)
            L++;
        else if(a[i]-i>0)
            R++;
        sum+=abs(a[i]-i);
    }
    ans=sum;
    for(int i=1;i<n;++i)
    {
        cnt[org+a[i]-1]--;
        if(a[i]-1>0)
            R--;
        sum+=abs(a[i]-n)-abs(a[i]-1);
        sum+=R+cnt[org]-L;
        R+=cnt[org],L-=cnt[org-1];
        org--;
        cnt[org+a[i]-n]++;
        if(a[i]-n<0)
            L++;
        ans=min(ans,sum);
    }
    printf("%lld
",ans);
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}



以上是关于test20181005 序列的主要内容,如果未能解决你的问题,请参考以下文章

20181005提高测试

论“浏览器”-20181005

20181005 js数组去重的三种方法

20181005 PHP 中 echo ,print,vardump 语句区别

Linux就该这么学 20181005(第八章防火墙)

Linux就该这么学 20181005(第七章磁盘管理)