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}\),导致 llcin/cout 很容易 TLE,再加上评测机波动同一份代码甚至可能有不同的得分,于是这题 90->60 了。所以这个惨案告诉我们当时限奇奇怪怪的时候,就赶紧用 intscanf/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;
}
posted @ 2024-08-07 11:56  Lithium_Chestnut  阅读(6)  评论(0)    收藏  举报