Codeforces Round #291 (Div. 2) E. Darth Vader and Tree (dp + 矩阵快速幂优化)

题目链接

题面:
在这里插入图片描述

题意:
有棵无限大的有根树,每个节点都有N个孩子,每个孩子距离父亲节点的距离为di.求距离根节点距离<=x的节点个数。

题解:
注意到 di <= 100
我们 设 dp [ i ] 为距离根节点为 i 的点的个数,sum [ i ] 为距离根节点小于等于 i 的点的个数。
cnt [ i ] 为距离父亲节点 为 i 的点的个数。
那么有转移方程。

for(int i=1;i<=100;i++)
{
	for(int j=1;j<=i;j++)
	{
		dp[i]=(dp[i]+dp[i-j]*cnt[j])%MOD;
	}
	sum[i]=(sum[i-1]+dp[i])%MOD;
}

注意到 1<=di<=100。
那么 dp [ x ] 只能由 dp[x - 1 ]----dp [ x - 100 ] 转移而来。
用矩阵快速幂优化。只保存一百项 dp,在转移的过程中同时计算 sum

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
#define pr make_pair
#define pb push_back
#define ui unsigned int
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define forhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int maxn=100100;
const int maxm=100100;
const int up=100000;
const int hashp=13331;
const int hashpp=131;

struct node
{
    ll a[110][110];
};

node operator * (const node &a,const node &b)
{
    node c;
    memset(c.a,0,sizeof(c.a));
    for(int i=1;i<=101;i++)
    {
        for(int j=1;j<=101;j++)
        {
            for(int k=1;k<=101;k++)
                c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
        }
    }
    return c;
}

node mypow(node a,ll b)
{
    node ans;
    memset(ans.a,0,sizeof(ans.a));
    for(int i=1;i<=101;i++)
        ans.a[i][i]=1;
    while(b)
    {
        if(b&1) ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}

ll d[maxn];
ll cnt[110],dp[110],sum[110];

int main(void)
{
    int n,x;
    scanf("%d%d",&n,&x);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&d[i]);
        cnt[d[i]]++;
    }
    sum[0]=dp[0]=1;
    for(int i=1;i<=100;i++)
    {
        for(int j=1;j<=i;j++)
            dp[i]=(dp[i]+dp[i-j]*cnt[j])%mod;
        sum[i]=(sum[i-1]+dp[i])%mod;
    }
    if(x<=100)
    {
        printf("%lld\n",sum[x]);
        return 0;
    }
    node ans;
    memset(ans.a,0,sizeof(ans.a));
    for(int i=1;i<=99;i++)
        ans.a[i][i+1]=1;
    for(int i=1;i<=100;i++)
        ans.a[101][i]=ans.a[100][i]=cnt[100-i+1];
    ans.a[101][101]=1;
    ans=mypow(ans,x-100);
    ll res=0;
    for(int i=1;i<=100;i++)
        res=(res+dp[i]*ans.a[101][i])%mod;
    res=(res+sum[100]*ans.a[101][101])%mod;
    printf("%lld\n",res);
    return 0;
}


于是,我又想到,这个dp和sum都是线性的,那么直接杜教BM可否?
于是直接打到 dp [ 1000 ] , sum [ 1000 ] 然后杜教。
比我矩阵快速幂快多了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
#define pr make_pair
#define pb push_back
#define ui unsigned int
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define forhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int maxn=100100;
const int maxm=100100;
const int up=100000;
const int hashp=13331;


namespace linear_seq
{
    #define SZ(x) ((int)(x).size())
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    const int N=10010;
    ll res[N],base[N],c[N],md[N];
    vector<int> Md;
    const ll mod=1e9+7;

    ll powmod(ll a,ll b)
    {
        ll res=1;
        a%=mod;
        for(;b;b>>=1)
        {
            if(b&1)
                res=res*a%mod;
            a=a*a%mod;
        }
        return res;
    }

    void mul(ll *a,ll *b,int k)
    {
        for(int i=0;i<k+k;++i) c[i]=0;
        for(int i=0;i<k;++i)
            if(a[i])for(int j=0;j<k ;++j)
                    c[i+j]=(c[i+j]+a[i]*b[j])%mod;

        for (int i=k+k-1;i>=k;i--)
            if(c[i])for(int j=0;j<(int)Md.size();++ j)
                    c[i-k+Md[j]]=(c[i-k+Md[j]]-c[i]*md[Md[j]])%mod;

        for(int i=0;i<k;++i) a[i]=c[i];
    }

    int solve(ll n,VI a,VI b)
    {
        ll ans=0,pnt=0;
        int k=SZ(a);
        for(int i=0;i<k;++i) md[k-1-i]=-a[i];
        md[k]=1;Md.clear();
        for(int i=0;i<k;++i)
            if(md[i]!=0)Md.push_back(i);

        for(int i=0;i<k;++i) res[i]=base[i]=0;
        res[0]=1;

        while ((1ll<<pnt)<=n)
            pnt++;

        for (int p=pnt;p>=0;p--)
        {
            mul(res,res,k);
            if ((n>>p)&1)
            {
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];
                res[0]=0;
                for(int j=0;j<(int)Md.size();++ j)
                    res[Md[j]]=(res[Md[j]]-res[k]*md[Md[j]])%mod;
            }
        }

        for(int i=0;i<k;i++) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }

    VI BM(VI s)
    {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        for(int n=0;n<(int)s.size();++n)
        {
            ll d=0;
            for(int i=0;i<L+1;++ i)
                d=(d+(ll)C[i]*s[n-i])%mod;
            if(d==0) ++m;
            else if(2*L<=n)
            {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m)C.push_back(0);
                for(int i=0;i<(int)B.size();++i) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            }
            else
            {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m)C.push_back(0);
                for(int i=0;i<(int)B.size();++ i) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }

    ll gao(VI a,ll n)
    {
        VI c=BM(a);
        c.erase(c.begin());
        for(int i=0;i<(int)c.size();++i) c[i]=(mod-c[i])%mod;
        return (ll)solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};

ll d[maxn];
ll cnt[1100],dp[1100],sum[1100];
vector<int>vc;
int main(void)
{
    int n,x;
    scanf("%d%d",&n,&x);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&d[i]);
        cnt[d[i]]++;
    }
    sum[0]=dp[0]=1;
    vc.pb(1);
    for(int i=1;i<=1000;i++)
    {
        for(int j=1;j<=min(i,100);j++)
            dp[i]=(dp[i]+dp[i-j]*cnt[j])%mod;
        sum[i]=(sum[i-1]+dp[i])%mod;
        vc.pb(sum[i]);
    }
    printf("%lld\n",linear_seq::gao(vc,x));
    return 0 ;
}








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于SpringBoot+Vue的乡政府管理系统是一个现代化的Web应用程序,它使用了当今流行的技术栈来实现高效的后端服务和交互式的前端界面。以下是该系统使用的主要技术和功能介绍: 技术栈: SpringBoot:一个快速开发的框架,用于构建独立的、生产级别的Spring应用程序。它简化了配置过程,提供了大量默认配置,使得项目启动和运行更加便捷。 Vue.js:一个渐进式的JavaScript框架,用于构建用户界面。它易于上手,同时能够与其它库或已有项目整合,为开发者提供灵活性。 MySQL:一个关系型数据库管理系统,用于存储和管理数据。它支持标准的SQL语言,并且具有高性能、稳定性和易用性的特点。 功能模块: 用户管理:包括用户注册、登录、权限控制等功能,确保系统的安全性和用户的身份验证。 信息发布:允许管理员发布公告、通知等信息,以便及时传达给相关人员。 文件管理:提供文件上传、下载、删除等功能,方便管理和共享文档资料。 数据统计:对系统中的数据进行统计和分析,生成报表和图表,帮助决策者做出明智的决策。 任务管理:支持任务的创建、分配、跟踪和完成情况的记录,提高工作的效率和协作性。 留言板:提供一个平台供用户之间进行交流和讨论,促进信息共享和问题解决。 日志记录:记录系统的运行情况和用户的操作行为,便于问题的排查和安全审计。 数据备份与恢复:定期备份数据并能够在需要时进行恢复,保障数据的完整性和可靠性。 系统设置:允许管理员对系统的各项参数进行配置和管理,以满足不同场景的需求。 以上是该乡政府管理系统的主要技术和功能介绍。通过这些技术和功能的整合,该系统能够提供高效、安全、便捷的管理服务,满足乡政府的日常工作需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值