2021.9.22考试总结[NOIP模拟59]

T1 柱状图

关于每个点可以作出两条斜率绝对值为\(1\)的直线。

将绝对值拆开,对在\(i\)左边的点\(j\),\(h_i-i=h_j-j\),右边则是把减号换成加号。

把每个点位置为横坐标,高度与位置的差或和为纵坐标扔到坐标系里,发现每条直线上下点数最接近时代价最小。

对每个点二分即可。

T1
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

namespace IO{
	inline int read(){
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	inline void write(LL x,char sp){
		char ch[20]; int len=0;
		if(x<0){ putchar('-'); x=~x+1; }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	}
	inline int max(int x,int y){ return x<y?y:x; }
	inline int min(int x,int y){ return x<y?x:y; }
	inline void swap(int& x,int& y){ x^=y^=x^=y; }
	inline void ckmax(int& x,int y){ x=x<y?y:x; }
	inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

const int NN=100010,L=-1e6,R=1e9+1e6;
int n,mx,id,h[NN];
LL ans;

struct segment_tree{
	int tot,root,lc[NN*40],rc[NN*40],siz[NN*40];
	LL sum[NN*40];
	void pushup(int rt){
		siz[rt]=siz[lc[rt]]+siz[rc[rt]];
		sum[rt]=sum[lc[rt]]+sum[rc[rt]];
	}
	void insert(int& rt,int l,int r,int pos,int v){
		if(!rt) rt=++tot;
		if(l==r){ siz[rt]+=v; sum[rt]+=1ll*pos*v; return; }
		int mid=(l+r)/2;
		if(mid<=0&&l<0) --mid;
		if(pos<=mid) insert(lc[rt],l,mid,pos,v);
		else insert(rc[rt],mid+1,r,pos,v);
		pushup(rt);
	}
	int querysz(int rt,int l,int r,int opl,int opr){
		if(!rt) return 0;
		if(l>=opl&&r<=opr) return siz[rt];
		int mid=(l+r)/2,res=0;
		if(mid<=0&&l<0) --mid;
		if(opl<=mid) res+=querysz(lc[rt],l,mid,opl,opr);
		if(opr>mid) res+=querysz(rc[rt],mid+1,r,opl,opr);
		return res;
	}
	LL querysm(int rt,int l,int r,int opl,int opr){
		if(!rt) return 0;
		if(l>=opl&&r<=opr) return sum[rt];
		int mid=(l+r)/2; LL res=0;
		if(mid<=0&&l<0) --mid;
		if(opl<=mid) res+=querysm(lc[rt],l,mid,opl,opr);
		if(opr>mid) res+=querysm(rc[rt],mid+1,r,opl,opr);
		return res;
	}
}tl,tr;

LL getans(int ht,int pos){
	LL res=0;
	res+=1ll*tl.querysz(tl.root,L,R,L,ht-pos-1)*(ht-pos)-tl.querysm(tl.root,L,R,L,ht-pos-1);
	res+=1ll*tr.querysz(tr.root,L,R,L,ht+pos-1)*(ht+pos)-tr.querysm(tr.root,L,R,L,ht+pos-1);
	res+=tl.querysm(tl.root,L,R,ht-pos+1,R)-1ll*tl.querysz(tl.root,L,R,ht-pos+1,R)*(ht-pos);
	res+=tr.querysm(tr.root,L,R,ht+pos+1,R)-1ll*tr.querysz(tr.root,L,R,ht+pos+1,R)*(ht+pos);
//	cout<<ht<<' '<<pos<<' '<<res<<endl;
	return res;
}

signed main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	n=read(); ans=1e18;
	for(int i=1;i<=n;i++){
		h[i]=read();
		if(h[i]>mx) mx=h[i], id=i;
		tr.insert(tr.root,L,R,h[i]+i,1);
	}
	for(int i=1;i<=n;i++){
		tl.insert(tl.root,L,R,h[i]-i, 1);
		tr.insert(tr.root,L,R,h[i]+i,-1);
		int l=max(i,n-i+1),r=max(l,mx+abs(id-i));
		while(l+1<r){
			int mid=l+r>>1;
			int upr=tl.querysz(tl.root,L,R,L,mid-i-1)+tr.querysz(tr.root,L,R,L,mid+i-1);
			int dwn=n-upr;
			if(upr<=dwn) l=mid;
			else r=mid;
		}
		ans=min(ans,min(getans(l,i),getans(r,i)));
	}
	write(ans,'\n');
	return 0;
}

T2 应急棍

随便找规律,发现每次在最小的正方形中从左到右先放中心再放边的中点。

随便写写式子,高精小数是不可能写的。

\(code:\)

T2
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long double DB;

namespace IO{
	inline int read(){
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	inline void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0){ putchar('-'); x=~x+1; }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	}
	inline int max(int x,int y){ return x<y?y:x; }
	inline int min(int x,int y){ return x<y?x:y; }
	inline void swap(int& x,int& y){ x^=y^=x^=y; }
	inline void ckmax(int& x,int y){ x=x<y?y:x; }
	inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

DB l,x,y,len;
int c,t,n,tmp,pw4[31],pw2[64];

signed main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	c=read(); t=read();
	scanf("%Lf",&l);
	pw2[0]=pw4[0]=1;
	for(int i=1;i<31;i++) pw4[i]=pw4[i-1]*4;
	for(int i=1;i<64;i++) pw2[i]=pw2[i-1]*2;
	while(t--){
		n=read();
		if(n==1) printf("%d %d\n",0,0);
		if(n==2) printf("%.10Lf %.10Lf\n",l,l);
		if(n==3) printf("%d %.10Lf\n",0,l);
		if(n==4) printf("%.10Lf %d\n",l,0);
		if(n<5) continue;
		n-=4; tmp=0;//cout<<n<<' '<<pw4[tmp]+2*pw2[tmp]*(pw2[tmp]+1)<<endl;
		while(n-pw4[tmp]-2*pw2[tmp]*(pw2[tmp]+1)>0){
			n-=pw4[tmp]+2*pw2[tmp]*(pw2[tmp]+1);
			++tmp;
		}
		len=l/(DB)pw2[tmp];
		if(n<=pw4[tmp]){
			x=len/2.0+(DB)((n-1)/pw2[tmp])*len;
			y=len/2.0+(DB)((n-1)%pw2[tmp])*len;
			printf("%.10Lf %.10Lf\n",x,y);
			continue;
		}
		else n-=pw4[tmp];
		x=len*(DB)((n-1)/(pw2[tmp+1]+1));//cout<<len<<' '<<x<<' '<<n<<' '<<pw2[tmp+1]<<endl;
		if((n-1)%(pw2[tmp+1]+1)>=pw2[tmp]) x+=len/2.0, y=len*(DB)((n-1)%(pw2[tmp+1]+1)-pw2[tmp]);
		else y=len*(DB)((n-1)%(pw2[tmp+1]+1))+len/2.0;
		printf("%.10Lf %.10Lf\n",x,y);
	}
	return 0;
}

T3 擒敌拳

每个矩形的贡献是关于位置的一次函数。

单调栈维护出矩形最大管辖区间后李超线段树处理。

前两天刚学的东西,真滴NB。

\(code:\)

T3
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	inline int read(){
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	inline void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0){ putchar('-'); x=~x+1; }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	}
	inline int max(int x,int y){ return x<y?y:x; }
	inline int min(int x,int y){ return x<y?x:y; }
	inline void swap(int& x,int& y){ x^=y^=x^=y; }
	inline void ckmax(int& x,int y){ x=x<y?y:x; }
	inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

const int NN=200010;
int n,top,h[NN],stk[NN],ans[NN],timr[NN],timl[NN];

namespace LC_segtree{
	#define ld rt<<1
	#define rd (rt<<1)|1
	struct segs{
		int l,r,k,b;
		segs(){}
		segs(int a,int e,int c,int d){ l=a; r=e; k=c; b=d; }
		int calc(int pos){ return b+k*pos; }
		int cross(segs x){ return (b-x.b)/(x.k-k); }
	}s[NN<<2];
	void build(int rt,int l,int r){
		s[rt]=segs(1,n,0,-1e18);
		if(l==r) return;
		int mid=l+r>>1;
		build(ld,l,mid);
		build(rd,mid+1,r);
	}
	void insert(int rt,int l,int r,segs v){//cout<<s[rt].k<<' '<<s[rt].b<<' '<<v.k<<' '<<v.b<<' '<<s[rt].calc(l)<<' '<<v.calc(l)<<endl;
		if(l==r){
			if(s[rt].calc(l)<v.calc(l)) s[rt]=v;
			return;
		}
		if(l>=v.l&&r<=v.r){
			if(s[rt].calc(l)<=v.calc(l)&&s[rt].calc(r)<=v.calc(r)) s[rt]=v;
			else if(s[rt].calc(l)<=v.calc(l)||s[rt].calc(r)<=v.calc(r)){
				int mid=l+r>>1;
				if(s[rt].calc(mid)<v.calc(mid)) swap(s[rt],v);
				if(s[rt].cross(v)<=mid) insert(ld,l,mid,v);
				if(s[rt].cross(v)>=mid) insert(rd,mid+1,r,v);
			}
		} else{
			int mid=l+r>>1;
			if(v.l<=mid) insert(ld,l,mid,v);
			if(v.r>mid) insert(rd,mid+1,r,v);
		}
	}
	int query(int rt,int l,int r,int pos){//cout<<pos<<' '<<s[rt].k<<' '<<s[rt].b<<' '<<s[rt].calc(pos)<<endl;
		if(l==r) return s[rt].calc(pos);
		int res=s[rt].calc(pos),mid=l+r>>1;
		if(pos<=mid) return max(res,query(ld,l,mid,pos));
		else return max(res,query(rd,mid+1,r,pos));
	}
} using namespace LC_segtree;

signed main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n=read(); build(1,1,n);
	for(int i=1;i<=n;i++) h[i]=read();
	for(int i=1;i<=n;i++){
		while(h[stk[top]]>h[i]) timr[stk[top--]]=i-1;
		stk[++top]=i;
	}
	while(top) timr[stk[top--]]=n;
	for(int i=n;i;i--){
		while(h[stk[top]]>h[i]) timl[stk[top--]]=i;
		stk[++top]=i;
	}
	for(int i=1;i<=n;i++){
//		cout<<timl[i]<<' '<<timr[i]<<endl;
		insert(1,1,n,segs(i,timr[i],h[i],-h[i]*timl[i]));
		ans[i]=max(ans[i-1],query(1,1,n,i));
	}
	for(int i=1;i<=n;i++) write(ans[i],i==n?'\n':' ');
	return 0;
}

T4 边数

太弱,待补(根本补不完

上一篇:jQuery,from标签,歪路子小技巧


下一篇:模拟59 考试总结