NOIP2011 提高组 解题报告

NOIP2011 提高组 解题报告

本次测试题目:

\[\Large 160 \ pts, \text{No.} 3. \]

哈哈哈哈哈哈哈史诗级废物哈哈哈哈哈哈哈哈哈哈哈

但是确实暴露了很多缺点,OK 开始我们的 Reaction~

T1 \(100 \ pts\) - 循环 / 遍历

看这个题号也知道是小签到。很快就做出来了。

#include<bits/stdc++.h>
const int N=1e4+4;
int n,x[N],y[N],xlen[N],ylen[N],qx,qy;
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n;
	for(int i=1;i<=n;++i) std::cin>>x[i]>>y[i]>>xlen[i]>>ylen[i];
	std::cin>>qx>>qy;
	for(int i=n;i;--i) if(x[i]<=qx&&x[i]+xlen[i]>=qx&&y[i]<=qy&&y[i]+ylen[i]>=qy) return std::cout<<i<<'\n',0;
	std::cout<<-1<<'\n';
	return 0;
}

T2 \(30 \ pts\) - 前缀和

真丢脸啊

一看这个题面,窝巢,又做过。然后会不了一点。

“前缀和还能这么用!” —— 2023.5.16

“前缀和还能这么用!” —— 2023.9.5

丢脸的一集。最后用 分块 做的,不过异常的没有 bug,在把 s 变成 long long 之后能拿到 \(70 \ pts\)

  • 如果有那种很简单但是看不出来正解的题,想不出来也要做,比如这次的分块。
  • 学知识不能 学太死,到现在也不知道前缀和这么用。
  • 考试结束之前务必检查一遍 数据范围

分块 \(\text{code}\)

#include<bits/stdc++.h>
#define ll long long
const int N=2e5+5,B=505;
int n,m,BLN,bel[N],l[N],r[N],blk[B][B];
ll s,w[N],v[N],sum[B][B],ans=0x3f3f3f3f3f3f3f3f;
inline void init() {
	BLN=sqrt(n);
	int cnt=0;
	for(int i=1;i<=n;++i) {
		bel[i]=(i-1)/BLN+1;
		if(bel[i-1]!=bel[i]) cnt=0;
		blk[bel[i]][++cnt]=i;
	}
	for(int i=1;i<=bel[n];++i) {
		std::sort(blk[i]+1,blk[i]+BLN+1,[&](int a,int b){ return w[a]<w[b]; });
		for(int j=BLN;j;--j) sum[i][j]=sum[i][j+1]+v[blk[i][j]];
	}
}
inline ll itvquery(int ord,int val) {
	int bl=bel[l[ord]]+1,br=bel[r[ord]]-1;
	ll x=0,y=0;
	if(bl-1==br+1) {
		for(int i=l[ord];i<=r[ord];++i) if(w[i]>=val) ++x,y+=v[i];
		return x*y;
	}
	if(bel[l[ord]-1]!=bl-1) --bl;
	else for(int i=l[ord];bel[i]==bl-1&&i<=r[ord];++i) if(w[i]>=val) ++x,y+=v[i];
	if(bel[r[ord]+1]!=br+1) ++br;
	else for(int i=std::max(br*BLN+1,l[ord]);bel[i]==br+1&&i<=r[ord];++i) if(w[i]>=val) ++x,y+=v[i];
	for(int i=bl;i<=br;++i) {
		int tmp=std::lower_bound(blk[i]+1,blk[i]+BLN+1,val,[&](int a,int b){ return w[a]<b; })-blk[i];
		x+=BLN-tmp+1,y+=sum[i][tmp];
	}
	return x*y;
}
inline ll query(int val) {
	ll ans=0;
	for(int i=1;i<=m;++i) ans+=itvquery(i,val);
	return ans;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n>>m>>s;
	for(int i=1;i<=n;++i) std::cin>>w[i]>>v[i];
	for(int i=1;i<=m;++i) std::cin>>l[i]>>r[i];
	init();
	int l=0,r=1e6+1;
	while(l+1<r) {
		int mid=l+r>>1;
		ll tmp=query(mid);
		if(tmp<s) ans=std::min(ans,s-tmp),r=mid;
		else if(tmp>s) ans=std::min(ans,tmp-s),l=mid;
		else { ans=0; break; }
	}
	std::cout<<ans<<'\n';
	return 0;
}

不过至少下次写分块有底了,以前最担心的就是分块查询写不好。

\(\text{AC Code}\)

#include<bits/stdc++.h>
#define ll long long
const int N=2e5+5;
int n,m,w[N],v[N],l[N],r[N];
ll s,cnt[N],sum[N],ans=0x3f3f3f3f3f3f3f;
inline ll query(int W) {
	ll ans=0;
	for(int i=1;i<=n;++i) cnt[i]=cnt[i-1],sum[i]=sum[i-1],w[i]>=W&&(++cnt[i],sum[i]+=v[i]);
	for(int i=1;i<=m;++i) ans+=(cnt[r[i]]-cnt[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
	return ans;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n>>m>>s;
	for(int i=1;i<=n;++i) std::cin>>w[i]>>v[i];
	for(int i=1;i<=m;++i) std::cin>>l[i]>>r[i];
	int l=0,r=1e6+1;
	while(l+1<r) {
		int mid=l+r>>1;
		ll tmp=query(mid);
		if(tmp<s) ans=std::min(ans,s-tmp),r=mid;
		else if(tmp>s) ans=std::min(ans,tmp-s),l=mid;
		else { ans=0; break; }
	}
	std::cout<<ans<<'\n';
	return 0;
}

T3 \(0\)

没考什么算法。我也没做对。真丢脸。

下次要 认真审题 啊兄弟……括号里的 也要看的。

当时没有考虑两个端点的情况,所以寄了。不过说到底我写的甚至是复杂度线性对数的,而标程是线性……

\(O(n \log n) \text{ version}\)

#include<bits/stdc++.h>
#define ll long long
#define cpy(x,y,z) memcpy(x,y,sizeof x[0] * z)
const int N=2e5+5,K=55;
int n,k,p,a[N],b[N],sum[N][K],legal[N],cnt;
ll ans;
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n>>k>>p;
	for(int i=1;i<=n;++i) std::cin>>a[i]>>b[i],b[i]<=p&&(legal[++cnt]=i); 
	for(int i=n;i;--i) cpy(sum[i],sum[i+1],k),++sum[i][a[i]];
	for(int i=1;i<=n;++i) {
		int pos=std::lower_bound(legal+1,legal+cnt+1,i)-legal;
		if(pos<=cnt) {
			if(legal[pos]==i) ans+=sum[legal[pos]+1][a[i]];
			else ans+=sum[legal[pos]][a[i]];
		}
	}
	std::cout<<ans<<'\n';
	return 0;
}

\(O(n) \text{ version}\)

#include<bits/stdc++.h>
int n,k,p,lastest,sum[55],cnt[55],last[55],ans;
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n>>k>>p;
	for(int i=1,col,cst;i<=n;++i) {
		std::cin>>col>>cst;
		if(cst<=p) lastest=i;
		if(lastest>=last[col]) sum[col]=cnt[col];
		last[col]=i,ans+=sum[col],++cnt[col];
	}
	std::cout<<ans<<'\n';
	return 0;
}

其实这个做法差点就想出来了。我想一个方法容易想的不够深入,想一会儿就把他否定了,下次应该往深想一下,遇到问题解决问题,不是逃避问题。

T4 \(30 \ pts\) - 模拟 / 爆搜

其实能看出我的代码稳定性正确性上还是相当可以的,赛时提交的代码是一点剪枝没有,还采用了非常暴力的实现方法,但是依然能拿到 30 分,且在赛后加上最优性剪枝就直接搞到了 \(50 \ pts\)。简单修改下落机制之后,就来到了 \(60 \ pts\)。万万没想到啊,最后的 \(40 \ pts\) 错在这里了。

for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) { std::cin>>map[1][i][j]; if(!map[1][i][j]) break; }

这个读入程序最多读 \(6\) 格。

啊啊啊啊啊啊!改了一个晚自习才发现这个。因为即使有 \(7\) 格结尾也有一个 \(0\),所以要加限制条件的话应该是 j<=8。我只能说很无语,我本来都没加限制条件,画蛇添足。

  • 最优性剪枝 \(+ \ 20 \ pts\)
  • 下落机制优化 \(+ \ 10 \ pts\)
  • 读入程序修改 \(+ \ 40 \ pts\)

最后又优化了一下机制,冲到了最优解第一页。

\(\text{Code - } 435 ms \text{ with O2}\)

#include<bits/stdc++.h>
int n,map[10][10][10],need[10][10];
struct answer { int x,y,dir; } ans[10];
inline bool removable(int ver,int x,int y,int dir) {
	if(!map[ver][x][y]) return 0;
	if(!dir) return map[ver][x+1][y]==map[ver][x][y]&&map[ver][x][y]==map[ver][x+2][y];
	else return map[ver][x][y]==map[ver][x][y+1]&&map[ver][x][y]==map[ver][x][y+2];
}
inline void check(int ver) {
	bool yes=0; int x=0;
	for(int i=1;i<=5;++i) {
		x=0;
		for(int j=1;j<=7;++j) {
			if(!map[ver][i][j]) ++x;
			else if(x) map[ver][i][j-x]=map[ver][i][j],map[ver][i][j]=0;
		}
	}
	for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) {
		if(i<4&&removable(ver,i,j,0)) need[i][j]=need[i+1][j]=need[i+2][j]=1,yes=1;
		if(j<6&&removable(ver,i,j,1)) need[i][j]=need[i][j+1]=need[i][j+2]=1,yes=1; 
	}
	if(!yes) return;
	for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) if(need[i][j]) map[ver][i][j]=0,need[i][j]=0;
	check(ver);
}
inline void move(int step,int x,int y,int dir) {
	memcpy(map[step+1],map[step],sizeof map[step]);
	std::swap(map[step+1][x][y],map[step+1][x+dir][y]);
	check(step+1);
}
bool dfs(int step) {
	if(step==n+1) {
		for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) if(map[step][i][j]) return 0;
		return 1;
	}
	for(int i=1;i<=5;++i) {
		for(int j=1;j<=7;++j) {
			if(!map[step][i][j]) break;
			for(int k:{1,-1}) {
				if((i==5&&k==1)||(i==1&&k==-1)||(k==-1&&map[step][i-1][j])) continue;
				if((k==1&&map[step][i][j]==map[step][i+1][j])||(k==-1&&map[step][i][j]==map[step][i-1][j])) continue;
				ans[step]={i,j,k},move(step,i,j,k);
				if(dfs(step+1)) return 1;
			}
		}
	}
	return 0;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr); std::cout.tie(nullptr);
	std::cin>>n;
	for(int i=1;i<=5;++i) for(int j=1;j<=8;++j) { std::cin>>map[1][i][j]; if(!map[1][i][j]) break; }
	if(dfs(1)) for(int i=1;i<=n;++i) std::cout<<ans[i].x-1<<' '<<ans[i].y-1<<' '<<ans[i].dir<<'\n';
	else std::cout<<-1<<'\n';
	return 0;
}
posted @ 2023-09-06 08:43  二两碘酊  阅读(19)  评论(0编辑  收藏  举报