2019-7-19 考试总结

A. 那一天我们许下约定

根据题目很容易得到$dp$式子:

用$F[i][j]$表示第$i$次取,取了(也可以是剩下)的$j$个饼干(可以不取)的方案数。

$F[i][j]=\sum \limits_{k=j-m+1}^{j} F[i-1][k]$,

最后统计答案。

这是一个暴力的打法。

正解的$dp$定义是$F[i][j]$表示取$i$次,取了$j$个饼干(不可以不取)的方案数。

$F[i][j]=\sum \limits_{k=j-m+1}^{j-1} F[i-1][k]$,

答案$ans=\sum F[i][n] \times C_d^i$,

也就是在$d$天中选出$i$天来给饼干,然后给完。

最后就是那个$C_d^i$怎么求了。

底数不变求组合数,

$C_d^i=\frac{d!}{i!(d-i)!}$,

$C_d^{i-1}=\frac{d!}{(i-1)!(d-i+1)!}$,

上边的式子比上下边的式子,得到比值是$\frac{d-i+1}{i}$,

所以就可以$O(n)$求组合数了。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define inv(x) (mi(x,mod-2,mod))
#define Maxn 2010
#define mod 998244353
#define Reg register
using namespace std;
long long n,m,d,anp;
long long C[Maxn],ans[Maxn][Maxn],sum[Maxn][Maxn];
long long mi(long long x,long long y,long long p)
{
    long long ans=1,base=x;
    while(y)
    {
        if(y&1) ans=(ans*base)%p;
        base=(base*base)%p;
        y>>=1;
    }
    return ans%mod;
}
int main()
{
    while(scanf("%lld%lld%lld",&n,&d,&m)==3)
    {
        if(!n&&!m&&!d) break;
        memset(ans,0,sizeof(ans));
        memset(sum,0,sizeof(sum));
        memset(C,0,sizeof(C));
        --m;
        C[0]=1;
        for(Reg int i=1;i<=min(n,d);++i)
            C[i]=C[i-1]*(1ll*(d-i+1)%mod)%mod*inv(i)%mod;
        for(Reg int i=1;i<=m;++i)
        {
            ans[1][i]=1;
            sum[1][i]=(sum[1][i-1]+ans[1][i])%mod;
        }
        for(Reg int i=m+1;i<=n;++i)
            sum[1][i]=sum[1][i-1];
        anp=ans[1][n]*C[1]%mod;
        for(Reg int i=2;i<=n;++i)
        {
            for(Reg int j=1;j<=n;++j)
            {
                ans[i][j]=(1ll*sum[i-1][j-1]-sum[i-1][max(0,j-m-1)]+mod)%mod;
                sum[i][j]=(1ll*sum[i][j-1]+ans[i][j])%mod;
            }
            anp=(anp+ans[i][n]*C[i])%mod;
        }
        printf("%lld\n",anp);
    }
    return 0;
}
View Code

 

 

B. 那一天她离我而去

正解是删掉与节点$1$相连的一条边然后跑最短路,

之后就是裸的最短路。

题解的下几句话每太看懂。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define min(x,y) ((x)<(y)?(x):(y))
#define nex(x) ((x)%2==0?((x)-1):((x)+1))
#define Maxn 10050
#define INF 0x7ffffffffff
#define Reg register
using namespace std;
int t,n,m,tot,fir[Maxn];
bool vis[Maxn];
long long dis[Maxn];
struct Tu {int st,ed,next; long long val;} lian[Maxn*8];
void add(int x,int y,long long z)
{
    lian[++tot].st=x;
    lian[tot].ed=y;
    lian[tot].val=z;
    lian[tot].next=fir[x];
    fir[x]=tot;
    return;
}
void init()
{
    tot=0;
    for(Reg int i=1;i<=n;++i)
        fir[i]=vis[i]=0;
    return;
}
void spfa(int x)
{
    memset(dis,0x7f,sizeof(dis));
    queue<int> q;
    q.push(x); vis[x]=1; dis[x]=0;
    while(!q.empty())
    {
        int x=q.front(); q.pop(); vis[x]=0;
        for(Reg int i=fir[x];i;i=lian[i].next)
        {
            if(dis[lian[i].ed]>dis[x]+lian[i].val)
            {
                dis[lian[i].ed]=dis[x]+lian[i].val;
                if(!vis[lian[i].ed])
                {
                    vis[lian[i].ed]=1;
                    q.push(lian[i].ed);
                }
            }
        }
    }
    return;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        long long z;
        for(Reg int i=1,x,y;i<=m;++i)
        {
            scanf("%d%d%lld",&x,&y,&z);
            add(x,y,z); add(y,x,z);
        }
        long long p,ans=INF*100;
        for(Reg int i=fir[1];i;i=lian[i].next)
        {
            p=lian[i].val;
            lian[i].val=lian[nex(i)].val=INF;
            spfa(lian[i].ed);
            ans=min(ans,dis[1]+p);
            lian[i].val=lian[nex(i)].val=p;
        }
        if(ans<INF) printf("%lld\n",ans);
        else printf("-1\n");
    }
    return 0;
}
View Code

 

 

总结:

考试开始时看了一眼$T1$,发现$T1$的$dp$转移很简单。

花了$10$分钟时间打了暴力,然后看了一眼数据范围。。。

$d$的范围很大,$10^{12}$,我下意识的想到矩阵乘,矩阵乘可以转移,

然后码了一个矩阵乘的代码,最后发现它可能是一个类似循环矩阵的矩阵,然后试了一下发现确实可以。

最后时间复杂度$O(n^2logn)$,和暴力拿的是一个分。

正解居然是组合数。。。

$T2$看起来像一个水题,求一个最小环,

考试时跑的是一个$dfs$,最后$TLE60$,考试之后$rank1$讲的是$IDA^{*}$,试了一下可以水到$90$,

正解就是删边然后跑最短路,之后好像是一个优化。

$T3$看了一眼没什么思路,然后打了一个暴力$TLE20$,

考试最后一个小时想到了好几个可能的解法,一开始想的就是要让正反连边,但是之后想的是拓扑,偏了。。

第二种解法我想的是二分图。。。。不说了。。。

但是好像我想的都不能统计方案数。。。

最后$30+60+20=110$。

加油。。。

 

posted @ 2019-07-20 15:19  Milk_Feng  阅读(164)  评论(0编辑  收藏  举报