AtCoder agc009_e题解

题目大意:

给定 n n n个0和 m m m个1,每次可以选择 k k k个数删去它们,然后把它们的平均数写上去。

求最后剩下的数有几种。

答案对 1 e 9 + 7 1e9+7 1e9+7取模

保证 n + m − 1 n+m-1 n+m1能被 k k k整除

s o l : sol: sol:

把这个问题转化为 k k k叉树的模型。

其有 n + m n+m n+m个叶子结点。

其中有 n n n个写着0, m m m个写着1。

对于非叶子节点,它的值就是它的儿子节点的平均值。

假设这 n n n个0的深度分别是 d n i dn_i dni m m m个1的深度为 d m i dm_i dmi,那么根节点的数就是
∑ i = 1 m ( 1 k ) d m i \sum_{i=1}^{m}\left(\frac{1}{k}\right)^{dm_i} i=1m(k1)dmi
显然,如果所有叶节点都是1的话那么根节点也是1,所以
∑ i = 1 m ( 1 k ) d m i + ∑ i = 1 n ( 1 k ) d n i = 1 \sum_{i=1}^{m}\left(\frac{1}{k}\right)^{dm_i}+\sum_{i=1}^{n}\left(\frac{1}{k}\right)^{dn_i}=1 i=1m(k1)dmi+i=1n(k1)dni=1
若满足这个条件的话,一定能构造出合法的 k k k叉树

现在问题成了求有多少个 q q q能写成 n n n ( 1 k ) d n (\frac{1}{k})^{dn} (k1)dn,多少个 1 − q 1-q 1q能写成 m m m ( 1 k ) d m (\frac{1}{k})^{dm} (k1)dm相加的形式。

将根节点的权值用 k k k进制小数表示如同 0 , z 1 , z 2 , z 3 , ⋯   , z n , 0 ≤ z i < k 0,z_1,z_2,z_3,\cdots,z_n,0\leq z_i< k 0,z1,z2,z3,,zn,0zi<k

不考虑进位的话 ∑ z = m \sum z=m z=m

考虑上进位的话,每次可以将 z i z_{i} zi减去 1 1 1,让 z i + 1 z_{i+1} zi+1加上 k k k

∑ z ≡ m ( m o d k − 1 ) \sum z \equiv m\pmod {k-1} zm(modk1)

假设小数有 l e n len len位,则 1 − q 1-q 1q的位数和应该是 ( l e n − 1 ) ( k − 1 ) + k − ∑ z = l e n ( k − 1 ) − ∑ z + 1 (len-1)(k-1)+k-\sum z=len(k-1)-\sum z+1 (len1)(k1)+kz=len(k1)z+1

f [ i ] [ j ] f[i][j] f[i][j]表示小数点后第 i i i位,每一位的和是 j j j

直接dp就行了。

由于小数的末尾不能是0,所以当第 i i i位的末尾是否是0也应该加进状态里面。

#include<bits/stdc++.h>
using namespace std;
#define f1(a,b,c) for(int c=a;c<=b;c++)
#define f2(a,b,c) for(int c=a;c>=b;c--)
#define f3(a,b,c) for(int c=a;c;c=b)
#define so1(a,n) sort(a+1,a+n+1,mycmp);
#define so2(a,n) sort(a+1,a+n+1);
#define ll long long
#define itn int
#define ubt int 
#define mp make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
const int twx=2e3+100;
const int MOD=1e9+7;
const int inf=0x3f3f3f3f;
ll read()
{
    ll sum=0;
    ll flag=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        {
            flag=-1;
        }
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=((sum*10)+c-'0');
        c=getchar();
    }
    return sum*flag;
}
int n,m,k;
int asd[twx<<1][twx][2];
int ans;
int sum[twx];
ll add(ll x,ll y)
{
    return x+y>=MOD?x+y-MOD:x+y;
}
ll sub(ll x,ll y)
{
    return x-y<0?x-y+MOD:x-y;
}
ll mul(ll x,ll y)
{
    return 1LL*x*y%MOD;
}
void init()
{
	n=read();
    m=read();
    k=read();
}
void work()
{
    asd[0][0][0]=1;
    f1(1,n+m,i)
    {
        sum[0]=add(asd[i-1][0][0],asd[i-1][0][1]);
        f1(1,n,j)
        {
            sum[j]=add(sum[j-1],add(asd[i-1][j][0],asd[i-1][j][1]));
        }
        f1(0,n,j)
        {
            asd[i][j][0]=add(asd[i-1][j][0],asd[i-1][j][1]);
            if(j)
            {
                asd[i][j][1]=sub(sum[j-1],(j-k>=0?sum[j-k]:0));
            }
        }
        f1(0,n,j)
        {
            if(j%(k-1)==n%(k-1)&&(i*(k-1)-j+1)%(k-1)==m%(k-1)&&i*(k-1)-j+1<=m)
            {
                ans=add(ans,asd[i][j][1]);
            }
        }
    }
}
void print()
{
	printf("%d\n",ans);
}
int main()
{
    init();
    work();
    print();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值