2019-09-28 Div3模拟赛题解

出给学弟的普及模拟赛,现在题解搬到这里来,估计也没人看了,坟贴一个。

T1 函数(function)

题意

给定两个自变量是 \(x\) 因变量是 \(y\) 的函数,这两个函数有可能为二次函数、一次函数或常函数。求两个函数图像交点个数。

对于 \(100\%\) 的数据,保证两个函数中每项的系数的绝对值不超过 \(100\)

算法一

基础题,注意判断为直线或者抛物线,再注意一下交点有无数个的情况,期望得分 \(100\) 分。

Code

#include <stdio.h>
using namespace std;

int a,b,c,a1,b1,c1,D;

int main()
{
    freopen("function.in","r",stdin),freopen("function.out","w",stdout);

    scanf("%d%d%d%d%d%d",&a,&b,&c,&a1,&b1,&c1),a-=a1,b-=b1,c-=c1;

    if (a==0)
    {
        if (b==0)
	{
		if (c==0) printf("W");
		else printf("0");
	}
	else printf("1");
    }
    else
    {
	D=b*b-4*a*c;
	if (D<0) printf("0");
	if (D==0) printf("1");
	if (D>0) printf("2");
    }
    
    return 0;
}

T2 麻将(majiang)

题意

有一种新型的麻将,这麻将没有饼条万这样的花色,只有一种花色,也没有字牌;序数也不限于 \(1\)\(9\) ,而是在 \(1\)\(n\) 的范围内。

关于新型麻将的概念:

胡牌:即完成的牌,由 \(3m+2\) 张牌组成,其中有一个对子(两张一样的牌)和 \(m\) 个顺子(三张序数相连的序数牌,如 \(234\)\(678\) )或刻子(三张一样的牌)。

听牌:一组听牌的牌是指一组 \(3m+1\) 张牌,且再加上某一张牌就可以组成胡牌。那一张加上的牌可以称为等待牌。

判断一副牌是不是听牌,若是,找出所有的等待牌。

对于 \(70\%\) 的数据,\(n \le 200,m \le 800\)
对于 \(100\%\) 的数据,\(9≤n≤400,4≤m≤1000\)

算法一

用深搜来寻找答案,我们先枚举每一张听牌,那么很显然,时间复杂度就是 \(O(n)\), 再用深搜来判断可否胡牌。

我们用 \(cnt_x\) 来表示数值为 \(x\) 的牌出现了多少次。那么我们就从 \(1\)\(n\) 枚举对子,再枚举刻子和顺子。那么深搜的时间复杂度是 \(O(2n^2)\),合起来就是 O\((2n^3)\),空间复杂度为 \(O(n)\),期望得分 \(70\) 分。

算法二

先枚举对子。

这是为何?两个原因:

  • 先凑出对子,听的牌如果是顺子就可能有两张听牌。
  • 确定对子容易确定自己的顺子或刻子要怎么拼凑。

枚举对子后,因为剩下的都是三张三张的,就容易枚举了,直接考虑刻子和顺子的两种情况即可。时间复杂度为 \(O(n^3)\),空间复杂度为 \(O(n)\),期望得分 \(100\) 分。

Code

#include <stdio.h>
#define il inline
using namespace std;
const int N=4005;

int n,m,tuc[N],tuf[N];

il int check()
{
    int i,j,o;
    for (i=1; i<=n; i++) if (tuc[i]>=2)
    {
        for (j=o=1,tuc[i]-=2; j<=n+2; j++) tuf[j]=tuc[j];
        for (j=1; j<=n+2; j++)
        {
            if (tuf[j]<0){o=0;break;}
            tuf[j]%=3,tuf[j+1]-=tuf[j],tuf[j+2]-=tuf[j];
        }  
        tuc[i]+=2; 
        if (o) return 1;
    }
    return 0;
}

int main()
{
    freopen("majiang.in","r",stdin),freopen("majiang.out","w",stdout);

    scanf("%d%d",&n,&m); int i,x,o=0;
    for (i=1; i<=3*m+1; i++) scanf("%d",&x),tuc[x]++;
    for (i=1; i<=n; i++)
    {
        tuc[i]++;
        if (check()) o=1,printf("%d ",i); 
        tuc[i]--;
    }
    if (!o) puts("NO");
    
    return 0;
}

T3 交作业(homework)

题意

一个人带着若干个物品奔跑,速度为每秒一个单位长度。有 \(n\) 个站点,第 \(i\) 个站点在位置 \(s_i\) 处,这个人要将 \(h_i\) 个物品放到此处,放下后这个人就不在带着这些物品奔跑了。

在时刻 \(t\) 把物品放到 \(i\) 站点处会增加 \(f_i\times t\) 的疲劳值,带着 \(k\) 个物品奔跑一个单位长度会增加 \(k\) 的疲劳值。

对于 \(20\%\) 的数据,\(n≤20\)
对于 \(70\%\) 的数据,\(n≤100\)
对于 \(100\%\) 的数据,\(n\le 1000,|s_i|≤100,1≤h_i,f_i≤40\)

算法一

暴力深搜,每次枚举往左走或者往右走,时间复杂度为 \(O(2^n)\),空间复杂度为 \(O(n)\),期望得分 \(20\) 分。

算法二

首先肯定先以 \(s\) 为关键字排序。

很显然的一点,如果你经过一个站点却不放东西一定不是最优的。那么不论何时,已经处理好的站点的编号一定是一个连续的区间。

自然联想到区间 DP。设 \(dp_{i,j}\) 表示编号从 \(i\)\(j\) 的站点都处理好时的最小疲惫值。但还有是在左边还是在右边的问题,于是再来一维: \(dp_{i,j,0/1}\) 表示编号从 \(i\)\(j\) 的老师都处理好且处在 \(i\)\(j\) 时的最小疲惫值。

\(dp_{i,j,1/0}\) 显然可以由 \(dp_{i+1,j,1/0}\)\(dp_{i,j-1,1/0}\) 转移过来。 设 \(H_i\) 表示 \(h_i\) 的前缀和,\(F_i\) 表示 \(f_i\) 的前缀和。那么如果第 \(i\)\(j\) 个站点已经处理完,此时带着 \(H_n-H_{i-1}+H_j\) 个物品,并且每秒疲劳值会增加 \(F_n-F_{i-1}+F_j\) 次。于是有转移方程:

\[f_{i,j,0}=\min\{f_{i+1,j,0}+(s_{i+1}-s_i)(F_n-F_i+F_j+H_n-H_i+H_j),f_{i+1,j,1}+(s_j-s_i)(F_n-F_i+F_j+H_n-H_i+H_j)\} \]

剩下的同理,DP 的复杂度为 \(O(n^2)\)

我们枚举一下出发点,总时间复杂度 \(O(n^3)\),空间复杂度为 \(O(n^2)\),期望得分 \(70\) 分。

算法三

发现根本不需要枚举出发点,直接将 \(f_{i,i,1/0}\) 全部设为 \(0\),然后 DP 即可,时空复杂度均为 \(O(n^2)\),期望得分 \(100\) 分。

Code

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define il inline
#define int long long
using namespace std;
const int N=1005,I=0X3f3f3f3f;

int n,F[N],H[N],f[N][N][2];
struct teacher{int s,h,f;}t[N];

il bool cmp(teacher a,teacher b){return a.s<b.s;}

signed main()
{
    freopen("homework.in","r",stdin),freopen("homework.out","w",stdout); 
	
    scanf("%lld",&n); int i,j,l;
    for (i=1; i<=n; i++) scanf("%lld%lld%lld",&t[i].s,&t[i].h,&t[i].f);
    sort(t+1,t+n+1,cmp);
    for (i=1; i<=n; i++) F[i]=F[i-1]+t[i].f,H[i]=H[i-1]+t[i].h;
    memset(f,I,sizeof f);
    for (i=1; i<=n; i++) f[i][i][0]=f[i][i][1]=0;
	
    for (l=2; l<=n; l++) for (i=1; (j=i+l-1)<=n; i++)
        f[i][j][0]=min(f[i+1][j][0]+(t[i+1].s-t[i].s)*(F[i]+F[n]-F[j]+H[i]+H[n]-H[j]),f[i+1][j][1]+(t[j].s-t[i].s)*(F[i]+F[n]-F[j]+H[i]+H[n]-H[j])),
	f[i][j][1]=min(f[i][j-1][1]+(t[j].s-t[j-1].s)*(F[i-1]+F[n]-F[j-1]+H[i-1]+H[n]-H[j-1]),f[i][j-1][0]+(t[j].s-t[i].s)*(F[i-1]+F[n]-F[j-1]+H[i-1]+H[n]-H[j-1]));
	
    printf("%lld",min(f[1][n][0],f[1][n][1]));
    
    return 0;
}

T4 数列的 F(seriesf)

改编自 2019 高中数学联赛二试第二题。

题意

有一个长度为 \(n\) 的整数数列 \({a_i}\) ,且满足 \(1=a_1≤a_2≤\cdots≤a_{n-1}≤a_n=m\)

\(f=(a_1^2+a_2^2+\cdots+a_{n-1}^2+a_n^2)-(a_1a_3+a_2a_4+\cdots+a_{n-2}a_n)\)

求出 \(f\) 的最小值 \(f_0\) ,并求出使 \(f=f_0\) 成立的 \(n\) 元组 \((a_1,a_2,\ldots,a_{n-1},a_n)\) 的个数。

两个数都模 \(1000000007\)

对于 \(50\%\) 的数据,\(m=5,5≤n≤10\)
对于 \(100\%\) 的数据,\(5≤m≤n≤100000\)\(m\) 为奇数。

算法一

直接暴力搜索每个位置填上什么数,时间复杂度为 \(O(n^m)\),空间复杂度为 \(O(n)\),期望得分 \(50\) 分。

算法二

以下是 恐怖的 数学推导。

由已知:

\[2f=a_1^2+a_2^2+a_{n-1}^2+a_n^2+\sum\limits_{i=1}^{n-2}{(a_{i+2}-a_i)^2} \]

因为 \(a_i(i=1,2,\cdots,n)\) 为整数且 \(1=a_1\le a_2\le \cdots\le a_{n-1}\le a_n=m\)
所以显然:

\[(a_{i+2}-a_i)^2\ge a_{i+2}-a_i,a_1^2\ge a_1,a_2^2\ge a_2 \]

那么:

\[a_1^2+a_2^2+\sum\limits_{i=1}^{n-3}{(a_{i+2}-a_i)^2}\le a_1+a_2+\sum\limits_{i=1}^{n-3}{(a_{i+2}-a_i)}=a_{n-2}-a_{n-1} \]

再结合 \(a_{n-2}\le a_{n-1}\le a_n=m\) 便有:

\[2f\ge (a_n-a_{n-2})^2+a_{n-1}^2+a_n^2+a_{n-2}+a_{n-1}\ge (m-a_{n-2})^2+a_{n-2}^2+m^2+2a_{n-2} \]

\[f\ge \frac{1}{2}(m^2-2ma_{n-2}+a_{n-2}^2+m^2+2a_{n-2})=a_{n-2}^2-(m-1)a_{n-2}+m^2=(a_{n-2}-\frac{m-1}{2})^2+\frac{3m^2+2m-1}{4} \]

又因为 \(m\) 为奇数,所以 \(\frac{m-1}{2}\)\(\frac{3m^2+2m-1}{4}\) 均为整数。故当 \(a_1=a_2=1,a_{n-1}=a_n=\frac{m-1}{2}\)\(a_{i+2}-a_i\in {0,1}(i=1,2,\cdots,n-3)\) 时,\(f\) 由最小值 \(f_0=\frac{3m^2+2m-1}{4}\)

考虑组数,若要使等号成立,则要有 \(1=a_1\le a_2\le \cdots\le a_{n-1}=\frac{m-1}{2}\),并且对于每个 \(k(1\le k\le \frac{m-1}{2})\)\(a_1,a_2,\cdots,a_{n-1}\) 中至少有两项等于 \(k\),易证这和上面的取等条件是等价的。
对于每个 \(k(1\le k\le \frac{m-1}{2})\),设 \(a_1,a_2,\cdots,a_{n-1}\) 中等于 \(k\) 的项数为 \(1+g_k\)\(g_k\) 是正整数。
则有 \((1+g_1)+(1+g_2)+\cdots+(1+g_{\frac{m-1}{2}})=n-1\),即 \(g_1+g_2+\cdots+g_{\frac{m-1}{2}}=\frac{2n-m-3}{2}\)
使等式成立的 \(\frac{m-1}{2}\) 元组 \((g_1,g_2,\cdots,g_{\frac{m-1}{2}})\) 的个数为 \(\binom{\frac{m-1}{2}}{\frac{2n-m-3}{2}}\),且每个 \((g_1,g_2,\cdots,g_{\frac{m-1}{2}})\) 对应唯一的一个 \((a_1,a_2,\cdots,a_n)\),所以组数是 \(\binom{\frac{m-1}{2}}{\frac{2n-m-3}{2}}\)

然后我们就做完了这个大结论题 (逃),期望得分 \(100\) 分。

#include <stdio.h>
#define il inline
#define int long long
using namespace std;
const int N=1e5+5,mod=1e9+7; 

int n,m,fac[N],iac[N];

il int ksm(int a,int b)
{
    int res=1;
    for ( ; b; b>>=1,a=a*a%mod) if (b&1) res=res*a%mod;
    return res;
}

il int C(int x,int y){return fac[x]*iac[y]%mod*iac[x-y]%mod;}

signed main()
{
    freopen("seriesf.in","r",stdin),freopen("seriesf.out","w",stdout);

    scanf("%lld%lld",&n,&m); int i;
    for (i=fac[0]=1; i<N; i++) fac[i]=fac[i-1]*i%mod;
    for (i=n,iac[n]=ksm(fac[n],mod-2); i>=1; i--) iac[i-1]=iac[i]*i%mod;
    
    printf("%lld\n%lld",(3ll*m*m+2ll*m-1ll)/4%mod,C((2*n-m-3)/2,(m-3)/2)); 
    
    return 0;
}
posted @ 2020-08-14 15:10  Peanut_Tang  阅读(193)  评论(0编辑  收藏  举报