当前位置: 首页 > 工具软件 > Joi > 使用案例 >

JOI 2022 Final

董良策
2023-12-01

星际蛋糕推销

先算出每一段蛋糕的长度,每次询问在原序列上二分查找或双指针即可。
时间复杂度 O ( N + Q ) O(N+Q) O(N+Q),空间复杂度 O ( N ) O(N) O(N)

#include<stdio.h>
#include<algorithm>
int a[200001];
long long sum[200001];
int main(){
	int n,q,d;
	scanf("%d",&n);
	for(register int i=1;i<=n;i++){
		scanf("%d",a+i);
		sum[i]=1;
		while((a[i]&1)==0){
			a[i]>>=1;
			sum[i]<<=1;
		}
		sum[i]+=sum[i-1];
	}
	scanf("%d",&q);
	for(register int i=0;i!=q;i++){
		long long x;
		scanf("%lld",&x);
		d=std::lower_bound(sum+1,sum+n+1,x)-sum;
		printf("%d\n",a[d]);
	}
	return 0;
}

自学

要最小值最大,二分答案。
根据二分出的答案,可以算出每门课需要翘课的数量或可以翘课的数量,检查课时是否足够即可。
时间复杂度 O ( N ( log ⁡ 2 M + log ⁡ 2 A ) ) O(N(\log_2M+\log_2A)) O(N(log2M+log2A)),空间复杂度 O ( N ) O(N) O(N)

#include<stdio.h>
#define R register int
#define L long long
int a[300000],b[300000];
inline bool Check(int n,int m,L x){
	L tot=(L)n*m;
	for(R i=0;i!=n;i++){
		if(a[i]>b[i]){
			L t=(x+a[i]-1)/a[i];
			if(t>m){
				L v=x-(L)m*a[i];
				tot-=(v+b[i]-1)/b[i]+m;
			}else{
				tot-=t;
			}
		}else{
			tot-=(x+b[i]-1)/b[i];
		}
		if(tot<0){
			return false;
		}
	}
	return true;
}
int main(){
	int n,m,ct;
	scanf("%d%d",&n,&m);
	for(R i=0;i!=n;i++){
		scanf("%d",a+i);
	}
	for(R i=0;i!=n;i++){
		scanf("%d",b+i);
	}
	L l=1,r=1000000000000000000,mid,ans;
	while(l<=r){
		mid=l+r>>1;
		if(Check(n,m,mid)==true){
			ans=mid;
			l=mid+1;
		}else{
			r=mid-1;
		}
	}
	printf("%lld",ans);
	return 0;
}

选举

先对州按B排序,然后最终的抉择一定是先从小到大选一些州的B,然后选一些州的A。注意到选出的州一定是排序后的一段前缀和后面一些零散的A。
设状态 f i , j f_{i,j} fi,j 表示前 i i i 个州选择 j j j 个B州的最小时间,再在外层枚举最终选了几个B州即可转移和更新答案案。
时间复杂度 O ( N 3 ) O(N^3) O(N3),空间复杂度 O ( N ) O(N) O(N)

#include<stdio.h>
#include<algorithm>
#define R register int
#define D double
#define I inline
#define INF 999999999
I void Min(D&x,const D y){
	if(x>y){
		x=y;
	}
}
struct State{
	int ValA,ValB;
	I void Read(){
		scanf("%d%d",&ValA,&ValB);
		if(ValB==-1){
			ValB=INF;
		}
	}
	I friend bool operator<(State A,State B){
		return A.ValB<B.ValB;
	}
}s[501];
D f[501],g[501];
int suf[502][501];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(R i=1;i<=n;i++){
		s[i].Read();
	}
	std::sort(s+1,s+n+1);
	for(R i=n;i!=0;i--){
		for(R j=n-i+1;j!=0;j--){
			suf[i][j]=suf[i+1][j-1]+s[i].ValA;
		}
		for(R j=1;j<=n-i;j++){
			if(suf[i+1][j]<suf[i][j]){
				suf[i][j]=suf[i+1][j];
			}
		}
	}
	D ans=suf[1][m];
	for(R i=1;i<=m;i++){
		D inv=1/(1.0+i);
		f[0]=0;
		for(R j=1;j<=n&&j<=m;j++){
			for(R k=0;k<=j&&k<=i;k++){
				g[k]=INF;
			}
			for(R k=0;k!=j&&k<=i;k++){
				Min(g[k],f[k]+inv*s[j].ValA);
				Min(g[k+1],f[k]+s[j].ValB/(1.0+k));
			}
			for(R k=0;k<=j&&k<=i;k++){
				f[k]=g[k];
			}
			if(j>=i){
				Min(ans,f[i]+inv*suf[j+1][m-j]);
			}
		}
	}
	printf("%.9lf",ans);
	return 0;
}

铁路旅行 2

借助线段树处理出ST表 f i , j , k f_{i,j,k} fi,j,k 表示从 i i i i + 2 j − 1 i+2^j-1 i+2j1 中的一个位置出发乘坐 2 k 2^k 2k 班车能到达的区间即可。
时间复杂度 O ( N log ⁡ 2 2 N + Q log ⁡ 2 N ) O(N \log_2^2N+Q \log_2N) O(Nlog22N+Qlog2N),空间复杂度 O ( N log ⁡ 2 2 N ) O(N \log_2^2N) O(Nlog22N)

#include<stdio.h>
#define R register int
#define I inline
#define N 100001
I void Min(int&x,const int y){
	if(x>y){
		x=y;
	}
}
I void Max(int&x,const int y){
	if(x<y){
		x=y;
	}
}
struct Segment{
	int Lf,Rt;
	I friend Segment operator+(Segment A,Segment B){
		Min(A.Lf,B.Lf);
		Max(A.Rt,B.Rt);
		return A;
	}
}f[17][N],g[17][17][N];
I auto Pair(int l,int r){
	Segment Res;
	Res.Lf=l;
	Res.Rt=r;
	return Res;
}
class SegmentTree{
	int MinV[400000],MaxV[400000],Len;
	I void InitTree(int p,int lf,int rt){
		if(lf==rt){
			MinV[p]=MaxV[p]=lf;
		}else{
			MinV[p]=N;
			InitTree(p<<1,lf,lf+rt>>1);
			InitTree(p<<1|1,lf+rt+2>>1,rt);
		}
	}
	I void Cover(int p,int lf,int rt,const int l,const int r,const int vl,const int vr){
		if(l<=lf&&rt<=r){
			Min(MinV[p],vl);
			Max(MaxV[p],vr);
		}else{
			int mid=lf+rt>>1;
			if(l<=mid){
				Cover(p<<1,lf,mid,l,r,vl,vr);
			}
			if(r>mid){
				Cover(p<<1|1,mid+1,rt,l,r,vl,vr);
			}
		}
	}
	I void GetAns(int p,int lf,int rt,const int x,Segment&Res){
		Res=Res+Pair(MinV[p],MaxV[p]);
		if(lf!=rt){
			int mid=lf+rt>>1;
			if(x>mid){
				GetAns(p<<1|1,mid+1,rt,x,Res);
			}else{
				GetAns(p<<1,lf,mid,x,Res);
			}
		}
	}
	public:
		I void Init(const int n){
			Len=n;
			InitTree(1,1,n);
		}
		I void Modify(int lf,int rt,int vl,int vr){
			Cover(1,1,Len,lf,rt,vl,vr);
		}
		I void GetSeg(int x,Segment&Res){
			Res.Lf=Res.Rt=x;
			GetAns(1,1,Len,x,Res);
		}
}SEG;
I void BuildST(const int n,int t){
	for(R i=1;i<=n;i++){
		g[t][0][i]=f[t][i];
	}
	for(R i=1;1<<i<=n;i++){
		for(R j=1;j<=n+1-(1<<i);j++){
			g[t][i][j]=g[t][i-1][j]+g[t][i-1][j+(1<<i-1)];
		}
	}
}
int Log2[N];
I auto GetSegment(int t,int l,int r){
	int p=Log2[r-l+1];
	return g[t][p][l]+g[t][p][r-(1<<p)+1];
}
int main(){
	int n,k,m,q,x,y,ans,l,r;
	scanf("%d%d%d",&n,&k,&m);
	for(R i=2;i<=n;i++){
		Log2[i]=Log2[i>>1]+1;
	}
	k--;
	SEG.Init(n);
	for(R i=0;i!=m;i++){
		scanf("%d%d",&x,&y);
		if(x<y){
			SEG.Modify(x,x+k<y?x+k:y,N,y);
		}else{
			SEG.Modify(x-k>y?x-k:y,x,y,0);
		}
	}
	for(R i=1;i<=n;i++){
		SEG.GetSeg(i,f[0][i]);
	}
	BuildST(n,0);
	for(R i=1;1<<i<=n;i++){
		for(R j=1;j<=n;j++){
			x=f[i-1][j].Lf;
			y=f[i-1][j].Rt;
			f[i][j]=GetSegment(i-1,x,y);
		}
		BuildST(n,i);
	}
	scanf("%d",&q);
	for(R i=0;i!=q;i++){
		scanf("%d%d",&x,&y);
		ans=1;
		l=r=x;
		for(R j=Log2[n];j!=-1;j--){
			Segment S=GetSegment(j,l,r);
			if(y<S.Lf||y>S.Rt){
				ans+=1<<j;
				l=S.Lf;
				r=S.Rt;
			}
		}
		Segment S=GetSegment(0,l,r);
		if(y<S.Lf||y>S.Rt){
			puts("-1");
		}else{
			printf("%d\n",ans);
		}
	}
	return 0;
}

沙堡 2

在选择的矩形区域连边,每个点向自己周围且在矩形内的权值最大且小于自己权值的点连边,然后统计矩形内边数即可。
每个点的连边情况和该点与边界的位置情况相关。一个点和每个边界的位置情况有距离为 0 , 1 0,1 0,1和不少于 2 2 2三种情况,四个方向共 81 81 81中情况。
枚举上下边界,左右边界处理时用前缀和优化。
如果 H > W H>W H>W 就交换两个方向即可。
时间复杂度 O ( H 2 W ) O(H^2W) O(H2W),空间复杂度 O ( H W ) O(HW) O(HW)

#include<stdio.h>
#include<vector>
using namespace std;
#define R register int
#define I inline
const int DIR[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
I void Max(int&x,const int y){
	if(x<y){
		x=y;
	}
}
I int Id(int a,int b,int c,int d){
	return a+b*3+c*9+d*27;
}
vector<int>H[223],Sum[81][233];
I int GetMax(int x,int y,bool a,bool b,bool c,bool d){
	int res=0;
	if(a==true&&H[x][y+1]<H[x][y]){
		res=H[x][y+1];
	}
	if(b==true&&H[x+1][y]<H[x][y]){
		Max(res,H[x+1][y]);
	}
	if(c==true&&H[x][y-1]<H[x][y]){
		Max(res,H[x][y-1]);
	}
	if(d==true&&H[x-1][y]<H[x][y]){
		Max(res,H[x-1][y]); 
	}
	return res;
}
I bool Check(int x,int y,int a,int b,int c,int d){
	if(a!=0&&H[x][y]==GetMax(x,y+1,a==2,b!=0,true,d!=0)||b!=0&&H[x][y]==GetMax(x+1,y,a!=0,b==2,c!=0,true)||c!=0&&H[x][y]==GetMax(x,y-1,true,b!=0,c==2,d!=0)||d!=0&&H[x][y]==GetMax(x-1,y,a!=0,true,c!=0,d==2)){
		return false;
	}
	return true;
}
I int GetSum(int t,int x,int y){
	if(x<0||y<0){
		return 0;
	}
	return Sum[t][x][y];
}
I int SumAll(int t,int lx,int rx,int ly,int ry){
	return GetSum(t,rx,ry)-GetSum(t,rx,ly-1)-GetSum(t,lx-1,ry)+GetSum(t,lx-1,ly-1);
}
I int CalcAll(int lx,int rx,int ly,int ry){
	int res=0;
	if(rx<lx+3){
		for(R i=lx;i<=rx;i++){
			for(R j=ly;j<=ry;j++){
				res+=SumAll(Id(ry-j,rx-i,j-ly,i-lx),i,i,j,j);
			}
		}
	}else{
		for(R i=ly;i<=ry;i++){
			res+=SumAll(Id(ry-i,2,i-ly,0),lx,lx,i,i)+SumAll(Id(ry-i,2,i-ly,1),lx+1,lx+1,i,i)+SumAll(Id(ry-i,2,i-ly,2),lx+2,rx-2,i,i)+SumAll(Id(ry-i,1,i-ly,2),rx-1,rx-1,i,i)+SumAll(Id(ry-i,0,i-ly,2),rx,rx,i,i);
		}
	}
	return res;
}
I int CalcColumn(int lx,int rx,int y,int ld,int rd){
	if(rx<lx+3){
		int res=0;
		for(R i=lx;i<=rx;i++){
			res+=SumAll(Id(rd,rx-i,ld,i-lx),i,i,y,y);
		}
		return res;
	}
	return SumAll(Id(rd,2,ld,0),lx,lx,y,y)+SumAll(Id(rd,2,ld,1),lx+1,lx+1,y,y)+SumAll(Id(rd,2,ld,2),lx+2,rx-2,y,y)+SumAll(Id(rd,1,ld,2),rx-1,rx-1,y,y)+SumAll(Id(rd,0,ld,2),rx,rx,y,y);
}
I int CalcPart(int lx,int rx,int ry){
	int res=0;
	if(rx<lx+3){
		for(R i=lx;i<=rx;i++){
			res+=SumAll(Id(2,rx-i,2,i-lx),i,i,0,ry);
		}
	}else{
		res=SumAll(Id(2,2,2,0),lx,lx,0,ry)+SumAll(Id(2,2,2,1),lx+1,lx+1,0,ry)+SumAll(Id(2,2,2,2),lx+2,rx-2,0,ry)+SumAll(Id(2,1,2,2),rx-1,rx-1,0,ry)+SumAll(Id(2,0,2,2),rx,rx,0,ry);
	}
	return res;
}
int ct[100001];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	if(n<m){
		for(R i=0;i!=n;i++){
			H[i].resize(m);
			for(R j=0;j!=m;j++){
				scanf("%d",&H[i][j]);
			}
		}
	}else{
		for(R i=0;i!=m;i++){
			H[i].resize(n);
		}
		for(R i=0;i!=n;i++){
			for(R j=0;j!=m;j++){
				scanf("%d",&H[j][i]);
			}
		}
		int t=n;
		n=m;
		m=t;
	}
	for(R i=0;i!=81;i++){
		for(R j=0;j!=n;j++){
			Sum[i][j].resize(m);
		}
	} 
	long long ans=0;
	for(R i=0;i!=n;i++){
		for(R j=0;j!=m;j++){
			for(R a=0;a!=3&&j+a!=m;a++){
				for(R b=0;b!=3&&i+b!=n;b++){
					for(R c=0;c!=3&&c<=j;c++){
						for(R d=0;d!=3&&d<=i;d++){
							Sum[Id(a,b,c,d)][i][j]=Check(i,j,a,b,c,d);
						}
					}
				}
			}
		}
	}
	for(R i=0;i!=81;i++){
		for(R j=0;j!=n;j++){
			for(R k=0;k!=m;k++){
				Sum[i][j][k]+=GetSum(i,j-1,k)+GetSum(i,j,k-1)-GetSum(i,j-1,k-1);
			}
		}
	}
	for(R i=0;i!=n;i++){
		for(R j=i;j!=n;j++){
			for(R k=0;k!=m;k++){
				for(R l=0;l!=3&&l<=k;l++){
					if(CalcAll(i,j,k-l,k)==1){
						ans++;
					}
				}
			}
			vector<int>A;
			for(R k=m-1;k>2;k--){
				int x=CalcColumn(i,j,k-3,0,2)+CalcColumn(i,j,k-2,1,2),c=CalcPart(i,j,k-2),y;
				y=CalcColumn(i,j,k,2,0)+CalcColumn(i,j,k-1,2,1);
				A.push_back(y+c);
				ct[y+c]++;
				c+=1-x;
				if(c>-1){
					ans+=ct[c];
				}
			}
			for(int T:A){
				ct[T]=0;
			}
		}
	}
	printf("%lld",ans);
	return 0;
}
 类似资料:

相关阅读

相关文章

相关问答