9.5模拟赛
score=60+0+30+0=90
我是真没想到第二题会挂60分,最后一道剪枝把正解剪掉了,挂了20。
别人剪枝暴力过60,我剪枝直接剪正解。
只能说数据还是出得挺好的,没想到剪枝+打表都会挂。
T1
前60分直接O(n^2)暴力
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+20;
int n;
long long a[maxn],b[maxn],c[maxn];
int main()
{
freopen("conv.in","r",stdin);
freopen("conv.out","w",stdout);
scanf("%d",&n);
for(int i=0;i<=n-1;++i) scanf("%lld",&a[i]);
for(int i=0;i<=n-1;++i) scanf("%lld",&b[i]);
for(int i=0;i<=n-1;++i)
for(int j=0;j<=n-1;++j)
c[i]=max(c[i],a[j]+b[(i-j+n)%n]);
for(int i=0;i<=n-1;++i) printf("%lld ",c[i]);
return 0;
}
/*
5
3 2 4 7 5
8 9 6 1 0
*/
我们的思维he很容易被枚举i,j限制,其实很多时候部分分甚至是正解都可以通过枚举数组中的值,同时结合方案数来解决问题。
看一眼值域就可以发现虽然n很大,但是a、b<=5000且a、b各自的总和都<=5000.
如果我们直接统计a、b数组中每个数出现的次数和位置,然后枚举值的大小,反过来在原数组中找值是否存在,若存在则直接更新,若不存在就跳过,不更新
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
const int M=5005;
int n,m,mx,numa[M],numb[M];
int a[N],b[N],c[N],pla[M][M],plb[M][M];
void checkmax(int &a,int b)
{
a=max(a,b);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i) scanf("%d",&a[i]);
for(int i=0;i<n;++i) scanf("%d",&b[i]);
for(int i=0;i<n;++i)
{
mx=max(mx,a[i]);
if(a[i])
{
pla[a[i]][++numa[a[i]]]=i;
}
}
for(int i=0;i<n;++i)
{
mx=max(mx,b[i]);
if(b[i])
{
plb[b[i]][++numb[b[i]]]=i;
}
}
for(int i=0;i<n;++i) c[i]=mx;
for(int aa=5000;aa>=1;aa--)
{
for(int p=1;p<=numa[aa];p++)
{
int j=pla[aa][p];
for(int bb=5000;bb>=1;bb--)
{
for(int q=1;q<=numb[bb];q++)
{
int k=plb[bb][q];
checkmax(c[(j+k)%n],aa+bb);
}
}
}
}
for(int i=0;i<n;++i) printf("%d ",c[i]);
return 0;
}
/*
5
3 2 4 7 5
8 9 6 1 0
*/
简单地转化了一下思维,就水过了一道题
T2
30分
一上来肯定就是传统手艺直接O(n^2)暴力
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+20,mod=1e9+7;
int n;
long long a[maxn];
long long sum=0;
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
sum=(sum+(long long)(a[i]^a[j])*(a[i]^a[j])%mod)%mod;
}
}
printf("%lld",sum);
return 0;
}
/*
3
2 5 4
*/
可惜我在考试的时候炸了30分,因为统计答案写成了sum+=(long long)(a[i]a[j])*(a[i]a[j])%mod)%mod
就是把sum+=写在取模外面,直接炸int
所以说需要取模的时候不要写sum+=sum%mod,一定要把所有变量都写在取模里面。
60分:我们看一眼数据范围,发现ai<=1000,所以直接再用一次T1用过的思路转换。
统计变量的每个值出现的次数,然后枚举值,乘上方案数
100分:二进制拆位
T3
前30分:直接每个点跑一次dijkstra,或者floyd,不过floyd要记得初始化
中间30:因为不需要删边,所以直接树形dp
最后40:还没搞懂
T4
前20:直接dfs
中间40:dp+枚举
后30:还没懂
总结:
(1)整体状态:暴力分写的很足,不过还是因为一些愚蠢的错误挂了60,建议下次多出几个大数据,以及把自己的代码仔细地多读几遍,最后每次考完回顾一下都有哪些应当避免的失误。
整体时间分配还可以,不过因为太急着拿暴力分,思维就有一些混乱,看一道题一段时间没进展就想赶紧跳下一道,有一些完全能切掉的题目根本就没深入思考。写完暴力以后的时间基本都浪费掉了,下一次如果能仔细思考一下分会高很多。
(2)做题经验:这套题的前160分都在强调同一个上面反复提到的问题:
思维不能局限于枚举i、j,很多时候转换思维枚举数列里的值,复杂度会优秀很多。
这个问题的本质是要关注每一个题目总体或者部分分给出的数据范围,因为所有的范围一定都是一个突破口,可以让暴力代码被优化,甚至是切题的突破口
剩余为完成代码仍在施工中