NOIP2011 提高组 解题报告
NOIP2011 提高组 解题报告
本次测试题目:
哈哈哈哈哈哈哈史诗级废物哈哈哈哈哈哈哈哈哈哈哈
但是确实暴露了很多缺点,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;
}