• 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))都是以讹传讹。

    后来学长告诉我吉司机线段树的复杂度上界被证明是(O(n log^3 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;
    }
    
    静渊以有谋,疏通而知事。
  • 相关阅读:
    【洛谷P2660烤鸡】
    cogs448
    排队打水
    洛谷U36590搬书
    NOIP2012借教室
    归并排序模版
    NOIP2015神奇的幻方
    NOIP2006能量项链
    NOIP2003加分二叉树
    NOI1995石子合并&多种石子合并
  • 原文地址:https://www.cnblogs.com/autoint/p/9745321.html
Copyright © 2020-2023  润新知