zoukankan      html  css  js  c++  java
  • 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;
    }
    
    静渊以有谋,疏通而知事。
  • 相关阅读:
    hihoCoder #1062 : 最近公共祖先·一
    hihoCoder #1050 : 树中的最长路
    hihoCoder #1049 : 后序遍历
    108 Convert Sorted Array to Binary Search Tree 将有序数组转换为二叉搜索树
    107 Binary Tree Level Order Traversal II 二叉树的层次遍历 II
    106 Construct Binary Tree from Inorder and Postorder Traversal 从中序与后序遍历序列构造二叉树
    105 Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树
    104 Maximum Depth of Binary Tree 二叉树的最大深度
    102 Binary Tree Level Order Traversal 二叉树的层次遍历
    101 Symmetric Tree 判断一颗二叉树是否是镜像二叉树
  • 原文地址:https://www.cnblogs.com/autoint/p/9745321.html
Copyright © 2011-2022 走看看