20191007GKurumi爆0记 2

by GKurumi

T1、那一天我们许下约定

题目描述:(保密)

思路分析,想了10多分钟,dp打了10多分钟,之后发现d原来那么大。

只能拿30分

30分代码(程序有点锅,不要贸然复制粘贴):

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,d,f[2001][2001];
int main()
{
    cin>>n>>d>>m;
    while(n&&m&&d)
    {
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for(int i=1;i<=d;i++)
        {
            f[0][i]=1;
        }
        for(int i=1;i<=n;i++)
        {
            for(int k=1;k<=d;k++)
            {
                for(int j=min(m-1,i);j>=0;j--)
                {
                    if(i>=j)f[i][k]=(f[i][k]+f[i-j][k-1])%mod;
                }
            }
        }
        cout<<f[n][d]<<endl;
        cin>>n>>d>>m;
    }
    return 0;
}
    

之后我们又想如何消掉d(考场先做的T2),我们定义f[ i ] [ j ],第一维存几天,第二维存给多少饼干的方案数。我们可得到一个式子。\(f[i][j] = \sum^{j - 1}_{k = j - m - 1}f[j - 1][k]\)

再往下想,直接遍历还是会超时,我们又需要一个前缀和算出每天给多少饼干的方案数。

处理完,算一遍方案数,不难想到,n如果小于d时,即使每天给一个的话也会有几天给不到,所以需先判断n于d的大小关系。统计方案就是吧多少天给多少饼干的方案加起来就ok。
\[ans = \sum^n_{i = 1}{f[i][j]}\times C\tbinom{i}{d}\]
这样思路就清晰了

之后放上代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='0')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
    return x*f;
}

const int mod=998244353;
int n,m,d,f[2010][2010];
int sum[4010],ans;
int ksm(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1)
        {
            ans=a*ans%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}

signed main()
{
//  freopen("contract.in","r",stdin);
//  freopen("contract.out","w",stdout);
    n=read();d=read();m=read();
    while(n!=0||m!=0||d!=0)
    {
        memset(f[1],0,sizeof(f[1]));
        f[0][0]=1;
        ans=0;
        for(int i=1;i<=min(n,m-1);i++)
        {
            f[1][i]=1;
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
            {
                sum[j]=((sum[j-1])%mod+(f[i-1][j])%mod)%mod;
            }
            for(int j=1;j<=n;j++)
            {
                f[i][j]=(sum[j-1]%mod-sum[max(j-m,0ll)]%mod+mod)%mod;
            }
        }
        int now=d%mod;
        for(int i=1;i<=min(n,d);i++)
        {
            if(i==1)
            {
                ans=((ans+now*f[i][n]%mod)%mod);
                ans%=mod;
            }
            else
            {
                now=(now%mod*ksm(i,mod-2)%mod*((d-i+1)%mod))%mod;
                ans%=mod;
                ans=(ans+f[i][n]%mod*now%mod)%mod;
            }
        }
        cout<<(ans+mod)%mod<<endl;
        n=read();d=read();m=read();
    }
    return 0;
}
            

T2、那一天她离我而去

题目描述:(加密)

思路清晰,找最小环:

不说了直接上代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,n,m,ans=0x3f3f3f3f;
int head[40010],cnt=1,to[40010],val[40010],nxt[40010];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='0')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
    return x*f;
}
void add(int u,int v,int w)
{
    val[++cnt]=w;
    to[cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
int dis[40010];
typedef pair<int,int> p;
priority_queue<p,vector<p>,greater<p> > q;
bool vis[40010];
void dij(int x)
{
    memset(dis,0,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[x]=0;
    q.push(p(0,x));
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[v]>dis[u]+val[i])
            {
                dis[v]=dis[u]+val[i];
                q.push(p(dis[v],v));

            }
        }
    }
}
signed main()
{
    freopen("leave.in","r",stdin);
    freopen("leave.out","w",stdout);
    t=read();
    while(t--)
    {

        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int u=read(),v=read(),w=read();
            add(u,v,w);add(v,u,w);
        }
        for(int i=head[1];i;i=nxt[i])
        {
            int tmp=val[i];
            val[i]=val[i^1]=0x3f3f3f3f;
            dij(1);
            ans=min(ans,dis[to[i]]+tmp);
            val[i]=val[i^1]=tmp;
        }

        if(ans==0x3f3f3f3f)ans=-1;
        //cout<<ans<<endl;
        printf("%lld\n",ans);
        ans=0x3f3f3f3f;
        cnt=1;
        memset(head,0,sizeof(head));
    }
    return 0;
}
/*
2
3 3
1 2 1
2 3 1
3 1 1
4 5
1 2 2
2 3 2
3 4 2
1 4 2
1 3 5
*/

n^2logn可能会T。

T3、哪一天她能重回我身边

题目描述:(加密)

神仙题,我这个蒟蒻是不会了,看题解是换根dp

大概思路就是:

第一次dp出以任意点u的答案 (只需要计算让所有边都指向父亲需要反转多少次)。

第二次dp出以每个点为根的答案。

取最小值,统计最小值的方案次数。

特别的: 基环树的方案只可能有1或两种,只需要判断顺逆时针的反转次数是否相同即可(具体来说只需要判断以环上相邻两点分别为根的答案是否相等即可)。

最后将每个联通块的最小值相加,方案数相乘。

上代码吧

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
using namespace std;
const int MAXN=100100,D=998244353;
int T;
int n;

struct node
{
    int to,nxt;
}mp[MAXN*2];
int h[MAXN*2],tot;
void add(int x,int y)
{
    mp[++tot].nxt=h[x];
    mp[tot].to=y;
    h[x]=tot;
}

bool flag;
int vcnt,ecnt;

bool vis[MAXN*2];

void dfs(int u)
{
    vcnt++;vis[u]=1;
    for(int i=h[u];i;i=mp[i].nxt)
    {
        int v=mp[i].to;
        ++ecnt;
        if(vis[v]) continue;
        dfs(v);
    }
}

int s,nxt,id;
ll num,f[MAXN*2],g[MAXN*2],ans,anscnt;

void dfs1(int u,int fa)
{
    vis[u]=1;
    for(int i=h[u];i;i=mp[i].nxt)
    {
        int v=mp[i].to;
        if(v==fa) continue;
        if(vis[v])
            s=u,nxt=v,id=i;
        else
        {
            dfs1(v,u);
            f[u]+=f[v]+(i&1);
        }
    }
}

vector<int> vv;

void dfs2(int u,int fa)
{
    vv.push_back(g[u]);
    for(int i=h[u];i;i=mp[i].nxt)
    {
        int v=mp[i].to;
        if(v==fa) continue;
        if(i==id||i==(id^1)) continue;
        g[v]=g[u]+((i&1)?-1:1);
        dfs2(v,u);
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        tot=1;flag=0;
        memset(h,0,sizeof(h));
        memset(vis,0,sizeof(vis));
        scanf("%d",&n);
        for(int i=1,aa,bb;i<=n;i++)
        {
            scanf("%d%d",&aa,&bb);
            add(bb,aa);add(aa,bb);
        }
        for(int i=1;i<=2*n;i++)
            if(!vis[i])
            {
                vcnt=ecnt=0;
                dfs(i);
                if(ecnt/2>vcnt)
                {
                    printf("-1 -1\n");
                    flag=1;
                    break;
                }
            }
        if(flag) continue;
        memset(vis,0,sizeof(vis));
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        ans=0;anscnt=1;
        for(int i=1;i<=2*n;i++)
            if(!vis[i])
            {
                vv.clear();
                s=nxt=id=0;
                num=0;

                dfs1(i,0);
                g[i]=f[i];
                dfs2(i,0);

                if(!id)
                {
                    sort(vv.begin(),vv.end());
                    for(int j=0;j<vv.size();j++)
                        if(vv[j]!=vv[0]) break;
                        else ++num;
                    ans+=vv[0];
                }
                else
                {
                    id&=1;
                    if(g[s]+(id^1)==g[nxt]+id) num=2;
                    else num=1;
                    ans+=min(g[s]+(id^1),g[nxt]+id);
                }
                anscnt=anscnt*num%D;
            }
        printf("%lld %lld\n",ans,anscnt);
    }
    return 0;
}

T3参考G_keng的博客,附上原链接

谢谢。

02-10 04:37