20240807
赛时得分
| 题目 | A | B | C | D | 总分 | 排名 | 比例 |
|---|---|---|---|---|---|---|---|
| 满分 | 100 | 100 | 100 | 100 | 400 | 176 | 100% |
| 得分 | 60 | 20 | 20 | 0 | 100 | 133 | 75.6% |
A. 优秀子矩阵(90/100)
真服了。出题人为了卡做法把时限调到 \(\text{400ms}\),导致 ll 和 cin/cout 很容易 TLE,再加上评测机波动同一份代码甚至可能有不同的得分,于是这题 90->60 了。所以这个惨案告诉我们当时限奇奇怪怪的时候,就赶紧用 int 和 scanf/printf 吧。
\(\text{90%}\) 得分做法,那其实这题我也不知道为啥 \(\mathcal{O}(n^4)\) 可以过掉 \(n,m\leq 200\) 的数据,在 luogu 上也是直接过了(luogu 原题时限是 \(1s\),是要开 ll 的)。做法比较好想,首先有一个前置知识叫做二维前缀和,原本我是不知道的,赛时第六感确实自己推出来了这个东西。那么二维前缀和是 \(S_{i,j}=S_{i-1,j}+S_{i,j-1}-S_{i-1,j-1}+A_{i,j}\),预处理完这个东西之后,我们四层循环枚举当前子矩形区域的右下角坐标 \((i,j)\),以及这个矩形的长宽 \(k,l\),可得到这个矩形内所有元素的和就是 \(S_{n-k+1,m-l+1}-S_{n-k-i+1,m-l+1}-S_{n-k+1,m-l-j+1}+S_{n-k-i+1,m-l-j+1}\),如果这个值大于 \(0\),那么平均值一定大于 \(0\),求所有符合条件的面积的 \(\max\) 就可以了。
其实枚举左上角和右下角的坐标是更直观的,但我赛时没想到。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=401;
int n,m,a[N][N],s[N][N],ans,maxn=-1,sq;
int main()
{
freopen("rec.in","r",stdin);
freopen("rec.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
for(int i=n;i>=1;i--)
{
for(int j=m;j>=1;j--)
{
for(int k=1;k<=n-i+1;k++)
{
for(int l=1;l<=m-j+1;l++)
{
ans=s[n-k+1][m-l+1]-s[n-k-i+1][m-l+1]-s[n-k+1][m-l-j+1]+s[n-k-i+1][m-l-j+1];
if(ans>=0)
{
sq=i*j;
maxn=max(maxn,sq);
}
}
}
}
}
printf("%d",maxn);
return 0;
}
B. 最大公约数(30/100)
其实我觉得题面加上一句答案相同的时候取字典序最小这句话。赛时就因为这个取了个等丢了 \(\text{10pts}\),本就不富裕的分数使我雪上加霜。
\(\text{30%}\) 得分做法,直接依题意模拟即可,枚举 \(i,j\) 从 \(1\) 到 \(n\),对于每一组数我们更新最大递归次数 \(maxn\) 和答案,最后输出就可以了。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
ll n,ans,maxn=-1,ins,a,b;
ll gcd(ll a,ll b)
{
if(!b) return a;
else
{
ans++;
return gcd(b,a%b);
}
}
int main()
{
freopen("gcd.in","r",stdin);
freopen("gcd.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
ans=0;
ins=gcd(i,j);
if(ans>maxn) a=i,b=j,maxn=ans;
}
}
cout<<a<<" "<<b;
return 0;
}
C. 排列逆序对(20/100)
\(\text{10%}\) 得分做法,是 Sub1 的分数,直接 next_permutation 枚举全排列,每次统计一下逆序对对数,如果 \(cnt=k\),就 ans++ 即可。
\(\text{20%}\) 得分做法,部分分是 Sub4(\(k=2\)),通过 Sub1 的程序多试几个数,可以得到规律 \(ans_i=ans_{i-1}+i-1\),这里要预处理一下 \(ans_3=4\)。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=5001,mod=1e9+7;
ll n,k,ans,a[N],cnt,p[N];
int main()
{
freopen("reverse.in","r",stdin);
freopen("reverse.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
if(k==2)
{
if(n==1 or n==2) cout<<0;
else
{
p[3]=2;
for(int i=4;i<=n;i++) p[i]=(p[i-1]+i-1)%mod;
cout<<p[n]%mod;
}
return 0;
}
for(int i=1;i<=n;i++) a[i]=i;
do
{
cnt=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(a[i]>a[j] and i<j) cnt++;
}
}
if(cnt==k) ans++;
}while(next_permutation(a+1,a+n+1));
cout<<ans%mod;
return 0;
}
D. 蓝色彼岸花(30/100)
震惊,lhm 开始学图论了。
好吧,其实读的出来暴力的做法直接把全图都遍历一遍就可以了。那如果比赛这么出题的话,我不得不学一些简单的图论了,毕竟送的分还是得要的,所以就在赛后学了链式前向星和图上 dfs。
那么本体属于带点权的无向图。我们在 dfs 的时候,为了计算最大的 ans 值,函数上要加入定义一个 \(w\),每次在 dfs 的开始统计一下 ans=max(ans,w),然后递归的 dfs 下一层 \(w\) 值变成 \(w+val_{to}\)。
注意各层初始化条件,建图的题,如果运行时莫名 RE,那么大多是 \(head\) 数组出了问题。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e5+1;
ll head[N],cnt,n,val[N],u,v,x,ans;
string op;
bool vis[N];
struct lhm
{
ll to,w,nxt;
}e[N];
void add(ll u,ll v)
{
cnt++;
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
return;
}
void dfs(ll u,ll w)
{
ans=max(ans,w);
vis[u]=1;
for(int i=head[u];~i;i=e[i].nxt)
{
if(!vis[e[i].to]) dfs(e[i].to,w+val[e[i].to]);
}
vis[u]=0;
return;
}
int main()
{
freopen("lycoris.in","r",stdin);
freopen("lycoris.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
head[0]=-1;
for(int i=1;i<=n;i++)
{
cin>>val[i];
head[i]=-1;
}
for(int i=1;i<n;i++)
{
cin>>u>>v;
add(u,v);
add(v,u);
}
while(1)
{
cin>>op;
if(op=="Done") return 0;
else if(op=="Query")
{
memset(vis,0,sizeof(vis));
ans=-0x3f3f3f;
cin>>u;
dfs(u,val[u]);
cout<<ans<<endl;
}
else if(op=="Change")
{
cin>>u>>x;
val[u]=x;
}
}
return 0;
}
\(\text{30%}\) 得分做法,因为数据水水过去了链特性的一个点。其实就是直接模拟加法最大值即可。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e5+1;
ll n,val[N],u,v,x,sum;
string op;
vector<ll> e[N];
bool pd;
void dfs(ll x,ll fa,ll ans)
{
sum=max(ans,sum);
for(auto y:e[x])
{
if(y!=fa) dfs(y,x,ans+val[y]);
}
return;
}
void sub()
{
while(1)
{
cin>>op;
if(op=="Done") return;
else if(op=="Change")
{
cin>>u>>x;
val[u]=x;
}
else
{
cin>>u;
ll ans1=0,ans2=0,maxn=val[u];
for(int i=u;i>=1;i--)
{
ans1+=val[i];
maxn=max(maxn,ans1);
}
for(int i=u;i<=n;i++)
{
ans2+=val[i];
maxn=max(maxn,ans2);
}
cout<<maxn<<endl;
}
}
return;
}
int main()
{
freopen("lycoris.in","r",stdin);
freopen("lycoris.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<n;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
if(min(u,v)!=i or max(u,v)!=i+1) pd=1;
}
if(pd==0)
{
sub();
return 0;
}
while(1)
{
cin>>op;
if(op=="Done") return 0;
else if(op=="Change")
{
cin>>u>>x;
val[u]=x;
}
else
{
sum=-1;
cin>>u;
dfs(u,0,val[u]);
cout<<sum<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号