test20181005 序列

时间:2023-03-09 02:35:49
test20181005 序列

题意

test20181005 序列

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("\n%lld\n",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\n",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}

然而并没有我想要的60分,1e5的数据我的程序要跑30s。

算了一下程序应该是\(O(n^{1.7})\)的,网上的\(O(n \log^2 n)\)都是以讹传讹。

后来学长告诉我吉司机线段树的复杂度上界被证明是\(O(n \log^3 n)\)的,非常有道理。

标解

test20181005 序列

解释一下题解所说的。

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\n",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\n",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}