3.24省选测试

最近有点犯神经...

$T1$

一开始想到了数位$dp$,基本上是对的,但是并不会求贡献了

就是这些位置任选的贡献怎么求,我被这部分卡住了

$f(i)$分为三种情况,分别是$i^0,i^1,i^2$

就是考虑从这这一位开始任选的贡献怎么求

for(int i=1;i<=50;i++)
{
    my_2[i]=my_2[i-1]*2;
    res0[i]=res0[i-1]*3%mod;
    //乘三是为什么?
    //首先比原来多了一位1
    //选择0,还是原来的贡献,否则,原来的每种情况都*2 
    res1[i]=(res1[i-1]*3+res0[i-1]*(my_2[i-1]%mod)%mod)%mod;
    //一次项也好说,选了,首先我们的m都乘了2,我们这
    res2[i]=(res2[i-1]*3+2*res1[i-1]*(my_2[i-1]%mod)%mod+res0[i-1]*(my_2[i-1]%mod)%mod*(my_2[i-1]%mod)%mod)%mod;
} 

 

$Sit_1:k=0$

我们这个是求所有的小于一个数的贡献,我们现在推完了$res[i-1]$.第$i-1$位开始任选的所有$f$之和

我们现在要求$res[i]$,显然我们可以把原来的贡献加上,考虑这一位是$1$,那么这时候我们多出来的贡献就是在原来的基础上,都多了一位$1$,这就相当于,前面的贡献$\times 2$

$Sit_2:k=1$

$f(x)=2^{len-1}\times x$

还是加上原来的贡献,我们对于前面那个式子,首先对应转移,我们前面式子乘了二,加上,后面的,对应的每一个$x$,还少一部分$x$,就是把每个$x$都多了$2^{len-1}\times$需要加几次

我发现我就是个推式子的菜鸡中的战斗鸡...

我们考虑这一位

$f(x+2^i)=2^{len}(x+2^i)$

$f(x)=2^{len-1}x$

$f(x+2^i)=f(x)\times 2+2^i\times 2^{len}$

我们现在要求的是原来的所有的$2^{len}$和就是$Sit_1$了

艹.我是不是傻啊...

$Sit_3:k=2$

简单点考虑,我们这个每一个都是一个$x^2$

貌似很难搞,前面不变

$f(x)=\sum_{i=0}^x C(x,i)\mod 2\times i^2$

$f(x+2^i)=\sum_{i=0}^{x+2^i} C(x+2^i,i)\mod 2\times i^2$

其实有值的位置是一样的,真的顿悟...

拆式子,我们多的是什么,$(x+a)^2=x^2+2\times a\times x + a^2$

然后和上一个一样推一下就好了

#include<bits/stdc++.h>
#define mod 1000000007
#define int long long 
#define MAXN 60
using namespace std;
int T,k,res0[MAXN],res1[MAXN],res2[MAXN],my_2[MAXN];
struct node
{
    int s0,s1,s2;
    friend node operator - (node a,node b)
    {
        return node{((a.s0-b.s0)%mod+mod)%mod,((a.s1-b.s1)%mod+mod)%mod,((a.s2-b.s2)%mod+mod)%mod};
    }
    friend node operator + (node a,node b)
    {
        return node{(a.s0+b.s0)%mod,(a.s1+b.s1)%mod,(a.s2+b.s2)%mod};
    }
};
node dp(int x,int wei)
{
    while(my_2[wei]>x) 
    {
        wei--;
    }
    //不是很理解,这个东西的计算方式
    //我想的是,既要考虑当前数字被那些数字包含,还要考虑这些包含的数字被哪些数字包含
    if (my_2[wei]==x) return node{res0[wei],res1[wei],res2[wei]};
    node ret1=dp(x-my_2[wei],wei),ret2=node{res0[wei],res1[wei],res2[wei]},ret3;
    ret3.s0=ret1.s0*2%mod;
    ret3.s1=(ret1.s1*2%mod+my_2[wei]%mod*ret1.s0%mod)%mod;
    ret3.s2=(ret1.s2*2%mod+my_2[wei]*2%mod*ret1.s1%mod+my_2[wei]%mod*ret1.s0%mod*(my_2[wei]%mod)%mod)%mod;
    return ret2+ret3;
}
void Init()
{
     res0[0]=1;
     res1[0]=0;
     res2[0]=0;
     my_2[0]=1;
     for(int i=1;i<=50;i++)
     {
        my_2[i]=my_2[i-1]*2;
        res0[i]=res0[i-1]*3%mod;
         //乘三是为什么?
        //首先比原来多了一位1
        //选择0,还是原来的贡献,否则,原来的每种情况都*2 
        res1[i]=(res1[i-1]*3+res0[i-1]*(my_2[i-1]%mod)%mod)%mod;
        //一次项也好说,选了,首先我们的m都乘了2,我们这
        res2[i]=(res2[i-1]*3+2*res1[i-1]*(my_2[i-1]%mod)%mod+res0[i-1]*(my_2[i-1]%mod)%mod*(my_2[i-1]%mod)%mod)%mod;
    } 
    cout<<res1[2]<<" "<<res1[3]<<endl;
}
signed main()
{
//    freopen("dialog.in","r",stdin);
//    freopen("dialog.out","w",stdout);
    Init();
    scanf("%lld %lld",&T,&k);
    for(int i=1,l,r;i<=T;++i)
    {
        scanf("%lld%lld",&l,&r);
        node Ans=dp(r+1,50)-dp(l,50);
        if(k==0) 
        {
            printf("%lld\n",Ans.s0);    
        }
        if(k==1) 
        {
            printf("%lld\n",Ans.s1);
        }
        if(k==2) 
        {
            printf("%lld\n",Ans.s2);
        }
    }
    return 0;
}

 

$T2$

一开始想了一个错误的贪心,把没访问到的节点按子树排序,每次选最大的

然后光荣的寄了

最简单的贪心推导,我们目前已经选了最大的点的父亲,我们下一步会选最大点,必然

我们每次找一个最大节点.和父亲合并,表示两个会一起选,这样就相当于,你每一步决策是选一堆点了,然后合并完之后得到新的点权之后再次合并就好了,贪心也太神了吧

#include<bits/stdc++.h>
#define int long long 
#define MAXN 100005
using namespace std;
vector<int>rd[MAXN];
int n,r,ans,f[MAXN],fa[MAXN],sum[MAXN],size[MAXN];
bool From[MAXN];
struct node
{
    int sum,size,pos;
    friend bool operator < (node a,node b)
    {
        return a.sum*b.size<a.size*b.sum;
    }
};
priority_queue<node>pq;
int Find(int x)
{
    if(f[x]==x) return x;
    return f[x]=Find(f[x]);
}
void dfs(int x,int f)
{
    fa[x]=f;
    for(int i=0;i<rd[x].size();++i)
    {
        int y=rd[x][i];
        if(y!=f) 
        {
           dfs(y,x);    
        }
    }
}
int news()
{
    int pos=pq.top().pos;
    while(!pq.empty()&&(pos==r||!From[pos]||sum[pos]!=pq.top().sum||size[pos]!=pq.top().size))
    {
        pq.pop();
        pos=pq.top().pos;
    }
    if(pq.empty()) return 0;
    return pos;
}
signed main()
{
    freopen("visit.in","r",stdin);
    freopen("visit.out","w",stdout);
    scanf("%lld %lld",&n,&r);
    for(int i=1;i<=n;++i) 
    {
        f[i]=i;
        size[i]=From[i]=1;
    }
    for(int i=1;i<=n;++i) scanf("%lld",&sum[i]);
    for(int i=1,u,v;i<n;++i)
    {
        scanf("%lld %lld",&u,&v);
        rd[u].push_back(v);
        rd[v].push_back(u);
    }
    dfs(r,0);
    for(int i=1;i<=n;++i) pq.push((node){sum[i],size[i],i});
    int pos;
    //大概明白我贪心错哪了,这里新造出来每个点的权值其实是平均值
    //我每次走的是一块,当着一块权值大我才走 
    while(pos=news())
    {
        pq.pop();
        int F=Find(fa[pos]);
        ans+=size[F]*sum[pos];
        size[F]+=size[pos];
        sum[F]+=sum[pos];
        f[pos]=F;
        pq.push((node){sum[F],size[F],F});
    }
    ans+=sum[r];
    printf("%lld\n",ans);
}

 

$T3$

有点不可做

posted @ 2022-03-24 20:23  Authentic_k  阅读(24)  评论(0编辑  收藏  举报